From b53ee71ea3b6f3df5833635f87d3a7b358d8bc44 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 18 Nov 2024 10:42:47 -0500 Subject: [PATCH 01/20] [Fleet] Fix agent policy namespace validation (#200258) --- .../agent_policy_advanced_fields/index.tsx | 4 +-- .../services/spaces/space_settings.test.ts | 34 ++++++++++++++++++- .../server/services/spaces/space_settings.ts | 2 +- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx index 0277184acabf2..305148584f545 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx @@ -375,8 +375,8 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent = > { }); }); }); + +describe('getSpaceSettings', () => { + function createSavedsClientMock(settingsAttributes?: any) { + const client = savedObjectsClientMock.create(); + + if (settingsAttributes) { + client.get.mockResolvedValue({ + attributes: settingsAttributes, + } as any); + } else { + client.get.mockRejectedValue( + SavedObjectsErrorHelpers.createGenericNotFoundError('Not found') + ); + } + + jest.mocked(appContextService.getInternalUserSOClientForSpaceId).mockReturnValue(client); + + return client; + } + it('should work with managedBy:null', async () => { + createSavedsClientMock({ + allowed_namespace_prefixes: ['test'], + managed_by: null, + }); + const res = await getSpaceSettings(); + + expect(res).toEqual({ + allowed_namespace_prefixes: ['test'], + managed_by: undefined, + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/spaces/space_settings.ts b/x-pack/plugins/fleet/server/services/spaces/space_settings.ts index ece0291ff4f7c..dff4df63b6a9d 100644 --- a/x-pack/plugins/fleet/server/services/spaces/space_settings.ts +++ b/x-pack/plugins/fleet/server/services/spaces/space_settings.ts @@ -36,7 +36,7 @@ export async function getSpaceSettings(spaceId?: string) { return { allowed_namespace_prefixes: settings?.attributes?.allowed_namespace_prefixes ?? [], - managed_by: settings?.attributes?.managed_by, + managed_by: settings?.attributes?.managed_by ?? undefined, }; } From 3c3f2c11a9da2a26f75f601ee365af1a227295b6 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 18 Nov 2024 16:45:25 +0100 Subject: [PATCH 02/20] [ES|QL] Don't suggest unsupported fields (#200544) ## Summary Closes https://github.com/elastic/kibana/issues/189666 Specifically: - we do not suggest unsupported fields - we add a warning if the field is used in the query ### How to test 1. Create an index with unsupported fields. I used this ``` PUT my-index-test-unsupported { "mappings" : { "properties" : { "my_histogram" : { "type" : "histogram" }, "my_text" : { "type" : "keyword" } } } } PUT my-index-test-unsupported/_doc/1 { "my_text" : "histogram_1", "my_histogram" : { "values" : [0.1, 0.2, 0.3, 0.4, 0.5], "counts" : [3, 7, 23, 12, 6] } } PUT my-index-test-unsupported/_doc/2 { "my_text" : "histogram_2", "my_histogram" : { "values" : [0.1, 0.25, 0.35, 0.4, 0.45, 0.5], "counts" : [8, 17, 8, 7, 6, 2] } } ``` 2. Go to the editor and try the autocomplete, it should not suggest the histogram field image 3. Use a query like `FROM my-index-test-unsupported | KEEP my_histogram `. You should see a warning image ### 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 --- .../src/__tests__/helpers.ts | 4 +-- .../src/autocomplete/__tests__/helpers.ts | 11 +++++-- .../src/autocomplete/autocomplete.test.ts | 1 + .../src/shared/resources_helpers.ts | 8 ++++- .../esql_validation_meta_tests.json | 11 +++++++ .../src/validation/validation.test.ts | 10 ++++++ .../src/validation/validation.ts | 33 ++++++++++--------- 7 files changed, 57 insertions(+), 21 deletions(-) diff --git a/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts index 2f46356acee37..02d2c062ccca7 100644 --- a/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts @@ -12,9 +12,7 @@ import { ESQLRealField } from '../validation/types'; import { fieldTypes } from '../definitions/types'; export const fields: ESQLRealField[] = [ - ...fieldTypes - .map((type) => ({ name: `${camelCase(type)}Field`, type })) - .filter((f) => f.type !== 'unsupported'), + ...fieldTypes.map((type) => ({ name: `${camelCase(type)}Field`, type })), { name: 'any#Char$Field', type: 'double' }, { name: 'kubernetes.something.something', type: 'double' }, { name: '@timestamp', type: 'date' }, diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts index 2221f4dc1582f..c49b05985c86a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts @@ -11,6 +11,7 @@ import { camelCase } from 'lodash'; import { parse } from '@kbn/esql-ast'; import { scalarFunctionDefinitions } from '../../definitions/generated/scalar_functions'; import { builtinFunctions } from '../../definitions/builtin'; +import { NOT_SUGGESTED_TYPES } from '../../shared/resources_helpers'; import { aggregationFunctionDefinitions } from '../../definitions/generated/aggregation_functions'; import { timeUnitsToSuggest } from '../../definitions/literals'; import { groupingFunctionDefinitions } from '../../definitions/grouping'; @@ -229,7 +230,11 @@ export function getFieldNamesByType( ) { const requestedType = Array.isArray(_requestedType) ? _requestedType : [_requestedType]; return fields - .filter(({ type }) => requestedType.includes('any') || requestedType.includes(type)) + .filter( + ({ type }) => + (requestedType.includes('any') || requestedType.includes(type)) && + !NOT_SUGGESTED_TYPES.includes(type) + ) .map(({ name, suggestedAs }) => suggestedAs || name); } @@ -267,7 +272,9 @@ export function createCustomCallbackMocks( enrichFields: string[]; }> ) { - const finalColumnsSinceLastCommand = customColumnsSinceLastCommand || fields; + const finalColumnsSinceLastCommand = + customColumnsSinceLastCommand || + fields.filter(({ type }) => !NOT_SUGGESTED_TYPES.includes(type)); const finalSources = customSources || indexes; const finalPolicies = customPolicies || policies; return { diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts index 5f3a2e45f9e1f..f8d72fecf229a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -887,6 +887,7 @@ describe('autocomplete', () => { 'FROM a | ENRICH policy /', ['ON $0', 'WITH $0', '| '].map(attachTriggerCommand) ); + testSuggestions( 'FROM a | ENRICH policy ON /', getFieldNamesByType('any') diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/resources_helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/resources_helpers.ts index 5e7d951d8bdbf..5659a585ed758 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/resources_helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/resources_helpers.ts @@ -12,6 +12,8 @@ import type { ESQLCallbacks } from './types'; import type { ESQLRealField } from '../validation/types'; import { enrichFieldsWithECSInfo } from '../autocomplete/utils/ecs_metadata_helper'; +export const NOT_SUGGESTED_TYPES = ['unsupported']; + export function buildQueryUntilPreviousCommand(ast: ESQLAst, queryString: string) { const prevCommand = ast[Math.max(ast.length - 2, 0)]; return prevCommand ? queryString.substring(0, prevCommand.location.max + 1) : queryString; @@ -54,7 +56,11 @@ export function getFieldsByTypeHelper(queryText: string, resourceRetriever?: ESQ return ( Array.from(cacheFields.values())?.filter(({ name, type }) => { const ts = Array.isArray(type) ? type : [type]; - return !ignored.includes(name) && ts.some((t) => types[0] === 'any' || types.includes(t)); + return ( + !ignored.includes(name) && + ts.some((t) => types[0] === 'any' || types.includes(t)) && + !NOT_SUGGESTED_TYPES.includes(type) + ); }) || [] ); }, diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index bf0e9782a3395..fee9f90f38c93 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -76,6 +76,10 @@ "name": "counterDoubleField", "type": "counter_double" }, + { + "name": "unsupportedField", + "type": "unsupported" + }, { "name": "dateNanosField", "type": "date_nanos" @@ -9690,6 +9694,13 @@ ], "warning": [] }, + { + "query": "from a_index | keep unsupportedField", + "error": [], + "warning": [ + "Field [unsupportedField] cannot be retrieved, it is unsupported or not indexed; returning null" + ] + }, { "query": "f", "error": [ diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index 9d737d542bd1a..68d8ebb233f5e 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -1695,6 +1695,16 @@ describe('validation logic', () => { ['Argument of [trim] must be [keyword], found value [doubleField] type [double]'] ); }); + + describe('unsupported fields', () => { + testErrorsAndWarnings( + `from a_index | keep unsupportedField`, + [], + [ + 'Field [unsupportedField] cannot be retrieved, it is unsupported or not indexed; returning null', + ] + ); + }); }); describe('Ignoring errors based on callbacks', () => { diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts index b4d095e2c0442..b3076d107f850 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts @@ -1223,21 +1223,24 @@ function validateFieldsShadowing( return messages; } -function validateUnsupportedTypeFields(fields: Map) { +function validateUnsupportedTypeFields(fields: Map, ast: ESQLAst) { + const usedColumnsInQuery: string[] = []; + + walk(ast, { + visitColumn: (node) => usedColumnsInQuery.push(node.name), + }); const messages: ESQLMessage[] = []; - for (const field of fields.values()) { - if (field.type === 'unsupported') { - // Removed temporarily to supress all these warnings - // Issue to re-enable in a better way: https://github.com/elastic/kibana/issues/189666 - // messages.push( - // getMessageFromId({ - // messageId: 'unsupportedFieldType', - // values: { - // field: field.name, - // }, - // locations: { min: 1, max: 1 }, - // }) - // ); + for (const column of usedColumnsInQuery) { + if (fields.has(column) && fields.get(column)!.type === 'unsupported') { + messages.push( + getMessageFromId({ + messageId: 'unsupportedFieldType', + values: { + field: column, + }, + locations: { min: 1, max: 1 }, + }) + ); } } return messages; @@ -1350,7 +1353,7 @@ async function validateAst( const variables = collectVariables(ast, availableFields, queryString); // notify if the user is rewriting a column as variable with another type messages.push(...validateFieldsShadowing(availableFields, variables)); - messages.push(...validateUnsupportedTypeFields(availableFields)); + messages.push(...validateUnsupportedTypeFields(availableFields, ast)); for (const [index, command] of ast.entries()) { const references: ReferenceMaps = { From 262b48f1cf4d4f624be99c2f42d169e4ab1f1f44 Mon Sep 17 00:00:00 2001 From: Sid Date: Mon, 18 Nov 2024 16:46:07 +0100 Subject: [PATCH 03/20] Fix issue with duplicate references in error object when copying saved objects to space (#200053) Closes https://github.com/elastic/kibana/issues/158027 ## Summary Simply dedupes references to objects if they are part of the missing_references in the copy saved objects to SO endpoint ### Notes - Update forEach over SOs to a regular for loop since we had a couple of early exit scenarios - Checks against the set for references already added to the missing list and adds only if not present ------ **Old response: Note the duplicate references** Screenshot 2024-11-14 at 01 52 54 **New response** Screenshot 2024-11-14 at 01 50 41 ### Release note Dedupe results from copy saved objects to spaces API when object contains references to other objects. --------- Co-authored-by: Elastic Machine --- .../import/lib/validate_references.test.ts | 32 ++++++++++++++++ .../src/import/lib/validate_references.ts | 37 +++++++++++-------- 2 files changed, 53 insertions(+), 16 deletions(-) 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 3702b46fdd790..6e3198f153df1 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 @@ -267,4 +267,36 @@ describe('validateReferences()', () => { 'Error fetching references for imported objects' ); }); + + // test that when references are missing returns only deduplicated errors + test('returns only deduplicated errors when references are missing', async () => { + const params = setup({ + objects: [ + { + id: '2', + type: 'visualization', + attributes: { title: 'My Visualization 2' }, + references: [ + { name: 'ref_0', type: 'index-pattern', id: '3' }, + { name: 'ref_0', type: 'index-pattern', id: '3' }, + ], + }, + ], + }); + params.savedObjectsClient.bulkGet.mockResolvedValue({ + saved_objects: [createNotFoundError({ type: 'index-pattern', id: '3' })], + }); + + const result = await validateReferences(params); + expect(result).toEqual([ + expect.objectContaining({ + type: 'visualization', + id: '2', + error: { + type: 'missing_references', + references: [{ type: 'index-pattern', id: '3' }], + }, + }), + ]); + }); }); diff --git a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts index e83fafe3348f7..b482bceb8ae0a 100644 --- a/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts +++ b/packages/core/saved-objects/core-saved-objects-import-export-server-internal/src/import/lib/validate_references.ts @@ -102,30 +102,35 @@ export async function validateReferences(params: ValidateReferencesParams) { const nonExistingReferenceKeys = await getNonExistingReferenceAsKeys(params); // Filter out objects with missing references, add to error object - objects.forEach(({ type, id, references, attributes }) => { - if (objectsToSkip.has(`${type}:${id}`)) { + for (const obj of objects) { + const { type, id, references, attributes } = obj; + const objectKey = `${type}:${id}`; + if (objectsToSkip.has(objectKey)) { // skip objects with retries that have specified `ignoreMissingReferences` - return; + continue; } - const missingReferences = []; - const enforcedTypeReferences = (references || []).filter(filterReferencesToValidate); + const missingReferences: Array<{ type: string; id: string }> = []; + const enforcedTypeReferences = references?.filter(filterReferencesToValidate) || []; + + const seenReferences = new Set(); for (const { type: refType, id: refId } of enforcedTypeReferences) { - if (nonExistingReferenceKeys.includes(`${refType}:${refId}`)) { + const refKey = `${refType}:${refId}`; + + if (nonExistingReferenceKeys.includes(refKey) && !seenReferences.has(refKey)) { missingReferences.push({ type: refType, id: refId }); + seenReferences.add(refKey); } } - if (missingReferences.length === 0) { - return; + if (missingReferences.length > 0) { + errorMap[objectKey] = { + id, + type, + meta: { title: attributes.title }, + error: { type: 'missing_references', references: missingReferences }, + }; } - const { title } = attributes; - errorMap[`${type}:${id}`] = { - id, - type, - meta: { title }, - error: { type: 'missing_references', references: missingReferences }, - }; - }); + } return Object.values(errorMap); } From f48ded9686505d63fe40427c26d6f2b22e462fa0 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Mon, 18 Nov 2024 09:49:10 -0600 Subject: [PATCH 04/20] Respect system dark mode in ESQL editor (#200233) When `uiSettings.overrides.theme:darkMode: true` is set, the ESQL editor uses dark mode. When `uiSettings.overrides.theme:darkMode: system` is set, the ESQL editor uses light mode, while the rest of the UI uses dark mode. Update the ESQL theme creation to create both light and dark variations of the theme at startup and use the theme in React component to determine which ESQL theme to use at runtime. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli --- .../kbn-esql-editor/src/esql_editor.test.tsx | 7 +- packages/kbn-esql-editor/src/esql_editor.tsx | 14 +- packages/kbn-monaco/index.ts | 2 +- packages/kbn-monaco/src/esql/index.ts | 4 +- packages/kbn-monaco/src/esql/lib/constants.ts | 3 +- .../src/esql/lib/esql_theme.test.ts | 8 +- .../kbn-monaco/src/esql/lib/esql_theme.ts | 316 +++++++++--------- packages/kbn-monaco/src/register_globals.ts | 5 +- 8 files changed, 187 insertions(+), 172 deletions(-) diff --git a/packages/kbn-esql-editor/src/esql_editor.test.tsx b/packages/kbn-esql-editor/src/esql_editor.test.tsx index ac00604e5508b..c572ff5355585 100644 --- a/packages/kbn-esql-editor/src/esql_editor.test.tsx +++ b/packages/kbn-esql-editor/src/esql_editor.test.tsx @@ -16,23 +16,20 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { ESQLEditor } from './esql_editor'; import type { ESQLEditorProps } from './types'; import { ReactWrapper } from 'enzyme'; -import { of } from 'rxjs'; +import { coreMock } from '@kbn/core/server/mocks'; describe('ESQLEditor', () => { const uiConfig: Record = {}; const uiSettings = { get: (key: string) => uiConfig[key], } as IUiSettingsClient; - const theme = { - theme$: of({ darkMode: false }), - }; const services = { uiSettings, settings: { client: uiSettings, }, - theme, + core: coreMock.createStart(), }; function renderESQLEditorComponent(testProps: ESQLEditorProps) { diff --git a/packages/kbn-esql-editor/src/esql_editor.tsx b/packages/kbn-esql-editor/src/esql_editor.tsx index e8ca582ac5229..636bb0b13ff17 100644 --- a/packages/kbn-esql-editor/src/esql_editor.tsx +++ b/packages/kbn-esql-editor/src/esql_editor.tsx @@ -25,7 +25,14 @@ import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { AggregateQuery } from '@kbn/es-query'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { ESQLLang, ESQL_LANG_ID, ESQL_THEME_ID, monaco, type ESQLCallbacks } from '@kbn/monaco'; +import { + ESQLLang, + ESQL_LANG_ID, + ESQL_DARK_THEME_ID, + ESQL_LIGHT_THEME_ID, + monaco, + type ESQLCallbacks, +} from '@kbn/monaco'; import memoize from 'lodash/memoize'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; @@ -91,7 +98,8 @@ export const ESQLEditor = memo(function ESQLEditor({ fieldsMetadata, uiSettings, } = kibana.services; - const timeZone = core?.uiSettings?.get('dateFormat:tz'); + const darkMode = core.theme?.getTheme().darkMode; + const timeZone = uiSettings?.get('dateFormat:tz'); const histogramBarTarget = uiSettings?.get('histogram:barTarget') ?? 50; const [code, setCode] = useState(query.esql ?? ''); // To make server side errors less "sticky", register the state of the code when submitting @@ -597,7 +605,7 @@ export const ESQLEditor = memo(function ESQLEditor({ vertical: 'auto', }, scrollBeyondLastLine: false, - theme: ESQL_THEME_ID, + theme: darkMode ? ESQL_DARK_THEME_ID : ESQL_LIGHT_THEME_ID, wordWrap: 'on', wrappingIndent: 'none', }; diff --git a/packages/kbn-monaco/index.ts b/packages/kbn-monaco/index.ts index ba8b0edb68e1a..283c3150302b7 100644 --- a/packages/kbn-monaco/index.ts +++ b/packages/kbn-monaco/index.ts @@ -20,7 +20,7 @@ export { } from './src/monaco_imports'; export { XJsonLang } from './src/xjson'; export { SQLLang } from './src/sql'; -export { ESQL_LANG_ID, ESQL_THEME_ID, ESQLLang } from './src/esql'; +export { ESQL_LANG_ID, ESQL_DARK_THEME_ID, ESQL_LIGHT_THEME_ID, ESQLLang } from './src/esql'; export type { ESQLCallbacks } from '@kbn/esql-validation-autocomplete'; export * from './src/painless'; diff --git a/packages/kbn-monaco/src/esql/index.ts b/packages/kbn-monaco/src/esql/index.ts index b14a2ab18ba75..64d49b155cc42 100644 --- a/packages/kbn-monaco/src/esql/index.ts +++ b/packages/kbn-monaco/src/esql/index.ts @@ -7,6 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export { ESQL_LANG_ID, ESQL_THEME_ID } from './lib/constants'; +export { ESQL_LANG_ID, ESQL_DARK_THEME_ID, ESQL_LIGHT_THEME_ID } from './lib/constants'; export { ESQLLang } from './language'; -export { buildESQlTheme } from './lib/esql_theme'; +export { buildESQLTheme } from './lib/esql_theme'; diff --git a/packages/kbn-monaco/src/esql/lib/constants.ts b/packages/kbn-monaco/src/esql/lib/constants.ts index b0b0588b3ff4a..56f2f85ab074e 100644 --- a/packages/kbn-monaco/src/esql/lib/constants.ts +++ b/packages/kbn-monaco/src/esql/lib/constants.ts @@ -8,6 +8,7 @@ */ export const ESQL_LANG_ID = 'esql'; -export const ESQL_THEME_ID = 'esqlTheme'; +export const ESQL_LIGHT_THEME_ID = 'esqlThemeLight'; +export const ESQL_DARK_THEME_ID = 'esqlThemeDark'; export const ESQL_TOKEN_POSTFIX = '.esql'; diff --git a/packages/kbn-monaco/src/esql/lib/esql_theme.test.ts b/packages/kbn-monaco/src/esql/lib/esql_theme.test.ts index 237996a7fbcaa..c2a200e650804 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_theme.test.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_theme.test.ts @@ -9,12 +9,12 @@ import { ESQLErrorListener, getLexer as _getLexer } from '@kbn/esql-ast'; import { ESQL_TOKEN_POSTFIX } from './constants'; -import { buildESQlTheme } from './esql_theme'; +import { buildESQLTheme } from './esql_theme'; import { CharStreams } from 'antlr4'; describe('ESQL Theme', () => { it('should not have multiple rules for a single token', () => { - const theme = buildESQlTheme(); + const theme = buildESQLTheme({ darkMode: false }); const seen = new Set(); const duplicates: string[] = []; @@ -40,7 +40,7 @@ describe('ESQL Theme', () => { .map((name) => name!.toLowerCase()); it('every rule should apply to a valid lexical name', () => { - const theme = buildESQlTheme(); + const theme = buildESQLTheme({ darkMode: false }); // These names aren't from the lexer... they are added on our side // see packages/kbn-monaco/src/esql/lib/esql_token_helpers.ts @@ -62,7 +62,7 @@ describe('ESQL Theme', () => { }); it('every valid lexical name should have a corresponding rule', () => { - const theme = buildESQlTheme(); + const theme = buildESQLTheme({ darkMode: false }); const tokenIDs = theme.rules.map((rule) => rule.token.replace(ESQL_TOKEN_POSTFIX, '')); const validExceptions = [ diff --git a/packages/kbn-monaco/src/esql/lib/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/esql_theme.ts index 330e55de86155..07a4d723b63e8 100644 --- a/packages/kbn-monaco/src/esql/lib/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/esql_theme.ts @@ -7,169 +7,177 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { euiThemeVars, darkMode } from '@kbn/ui-theme'; +import { euiDarkVars, euiLightVars } from '@kbn/ui-theme'; import { themeRuleGroupBuilderFactory } from '../../common/theme'; import { ESQL_TOKEN_POSTFIX } from './constants'; import { monaco } from '../../monaco_imports'; const buildRuleGroup = themeRuleGroupBuilderFactory(ESQL_TOKEN_POSTFIX); -export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ - base: darkMode ? 'vs-dark' : 'vs', - inherit: true, - rules: [ - // base - ...buildRuleGroup( - [ - 'explain', - 'ws', - 'assign', - 'comma', - 'dot', - 'opening_bracket', - 'closing_bracket', - 'quoted_identifier', - 'unquoted_identifier', - 'pipe', - ], - euiThemeVars.euiTextColor - ), +export const buildESQLTheme = ({ + darkMode, +}: { + darkMode: boolean; +}): monaco.editor.IStandaloneThemeData => { + const euiThemeVars = darkMode ? euiDarkVars : euiLightVars; - // source commands - ...buildRuleGroup( - ['from', 'row', 'show'], - euiThemeVars.euiColorPrimaryText, - true // isBold - ), + return { + base: darkMode ? 'vs-dark' : 'vs', + inherit: true, + rules: [ + // base + ...buildRuleGroup( + [ + 'explain', + 'ws', + 'assign', + 'comma', + 'dot', + 'opening_bracket', + 'closing_bracket', + 'quoted_identifier', + 'unquoted_identifier', + 'pipe', + ], + euiThemeVars.euiTextColor + ), - // commands - ...buildRuleGroup( - [ - 'dev_metrics', - 'metadata', - 'mv_expand', - 'stats', - 'dev_inlinestats', - 'dissect', - 'grok', - 'keep', - 'rename', - 'drop', - 'eval', - 'sort', - 'by', - 'where', - 'not', - 'is', - 'like', - 'rlike', - 'in', - 'as', - 'limit', - 'dev_lookup', - 'null', - 'enrich', - 'on', - 'with', - 'asc', - 'desc', - 'nulls_order', - ], - euiThemeVars.euiColorAccentText, - true // isBold - ), + // source commands + ...buildRuleGroup( + ['from', 'row', 'show'], + euiThemeVars.euiColorPrimaryText, + true // isBold + ), - // functions - ...buildRuleGroup(['functions'], euiThemeVars.euiColorPrimaryText), + // commands + ...buildRuleGroup( + [ + 'dev_metrics', + 'metadata', + 'mv_expand', + 'stats', + 'dev_inlinestats', + 'dissect', + 'grok', + 'keep', + 'rename', + 'drop', + 'eval', + 'sort', + 'by', + 'where', + 'not', + 'is', + 'like', + 'rlike', + 'in', + 'as', + 'limit', + 'dev_lookup', + 'null', + 'enrich', + 'on', + 'with', + 'asc', + 'desc', + 'nulls_order', + ], + euiThemeVars.euiColorAccentText, + true // isBold + ), - // operators - ...buildRuleGroup( - [ - 'or', - 'and', - 'rp', // ')' - 'lp', // '(' - 'eq', // '==' - 'cieq', // '=~' - 'neq', // '!=' - 'lt', // '<' - 'lte', // '<=' - 'gt', // '>' - 'gte', // '>=' - 'plus', // '+' - 'minus', // '-' - 'asterisk', // '*' - 'slash', // '/' - 'percent', // '%' - 'cast_op', // '::' - ], - euiThemeVars.euiColorPrimaryText - ), + // functions + ...buildRuleGroup(['functions'], euiThemeVars.euiColorPrimaryText), - // comments - ...buildRuleGroup( - [ - 'line_comment', - 'multiline_comment', - 'expr_line_comment', - 'expr_multiline_comment', - 'explain_line_comment', - 'explain_multiline_comment', - 'project_line_comment', - 'project_multiline_comment', - 'rename_line_comment', - 'rename_multiline_comment', - 'from_line_comment', - 'from_multiline_comment', - 'enrich_line_comment', - 'enrich_multiline_comment', - 'mvexpand_line_comment', - 'mvexpand_multiline_comment', - 'enrich_field_line_comment', - 'enrich_field_multiline_comment', - 'lookup_line_comment', - 'lookup_multiline_comment', - 'lookup_field_line_comment', - 'lookup_field_multiline_comment', - 'show_line_comment', - 'show_multiline_comment', - 'setting', - 'setting_line_comment', - 'settting_multiline_comment', - 'metrics_line_comment', - 'metrics_multiline_comment', - 'closing_metrics_line_comment', - 'closing_metrics_multiline_comment', - ], - euiThemeVars.euiColorDisabledText - ), + // operators + ...buildRuleGroup( + [ + 'or', + 'and', + 'rp', // ')' + 'lp', // '(' + 'eq', // '==' + 'cieq', // '=~' + 'neq', // '!=' + 'lt', // '<' + 'lte', // '<=' + 'gt', // '>' + 'gte', // '>=' + 'plus', // '+' + 'minus', // '-' + 'asterisk', // '*' + 'slash', // '/' + 'percent', // '%' + 'cast_op', // '::' + ], + euiThemeVars.euiColorPrimaryText + ), - // values - ...buildRuleGroup( - [ - 'quoted_string', - 'integer_literal', - 'decimal_literal', - 'named_or_positional_param', - 'param', - 'timespan_literal', - ], - euiThemeVars.euiColorSuccessText - ), - ], - colors: { - 'editor.foreground': euiThemeVars.euiTextColor, - 'editor.background': euiThemeVars.euiColorEmptyShade, - 'editor.lineHighlightBackground': euiThemeVars.euiColorLightestShade, - 'editor.lineHighlightBorder': euiThemeVars.euiColorLightestShade, - 'editor.selectionHighlightBackground': euiThemeVars.euiColorLightestShade, - 'editor.selectionHighlightBorder': euiThemeVars.euiColorLightShade, - 'editorSuggestWidget.background': euiThemeVars.euiColorEmptyShade, - 'editorSuggestWidget.border': euiThemeVars.euiColorEmptyShade, - 'editorSuggestWidget.focusHighlightForeground': euiThemeVars.euiColorEmptyShade, - 'editorSuggestWidget.foreground': euiThemeVars.euiTextColor, - 'editorSuggestWidget.highlightForeground': euiThemeVars.euiColorPrimary, - 'editorSuggestWidget.selectedBackground': euiThemeVars.euiColorPrimary, - 'editorSuggestWidget.selectedForeground': euiThemeVars.euiColorEmptyShade, - }, -}); + // comments + ...buildRuleGroup( + [ + 'line_comment', + 'multiline_comment', + 'expr_line_comment', + 'expr_multiline_comment', + 'explain_line_comment', + 'explain_multiline_comment', + 'project_line_comment', + 'project_multiline_comment', + 'rename_line_comment', + 'rename_multiline_comment', + 'from_line_comment', + 'from_multiline_comment', + 'enrich_line_comment', + 'enrich_multiline_comment', + 'mvexpand_line_comment', + 'mvexpand_multiline_comment', + 'enrich_field_line_comment', + 'enrich_field_multiline_comment', + 'lookup_line_comment', + 'lookup_multiline_comment', + 'lookup_field_line_comment', + 'lookup_field_multiline_comment', + 'show_line_comment', + 'show_multiline_comment', + 'setting', + 'setting_line_comment', + 'settting_multiline_comment', + 'metrics_line_comment', + 'metrics_multiline_comment', + 'closing_metrics_line_comment', + 'closing_metrics_multiline_comment', + ], + euiThemeVars.euiColorDisabledText + ), + + // values + ...buildRuleGroup( + [ + 'quoted_string', + 'integer_literal', + 'decimal_literal', + 'named_or_positional_param', + 'param', + 'timespan_literal', + ], + euiThemeVars.euiColorSuccessText + ), + ], + colors: { + 'editor.foreground': euiThemeVars.euiTextColor, + 'editor.background': euiThemeVars.euiColorEmptyShade, + 'editor.lineHighlightBackground': euiThemeVars.euiColorLightestShade, + 'editor.lineHighlightBorder': euiThemeVars.euiColorLightestShade, + 'editor.selectionHighlightBackground': euiThemeVars.euiColorLightestShade, + 'editor.selectionHighlightBorder': euiThemeVars.euiColorLightShade, + 'editorSuggestWidget.background': euiThemeVars.euiColorEmptyShade, + 'editorSuggestWidget.border': euiThemeVars.euiColorEmptyShade, + 'editorSuggestWidget.focusHighlightForeground': euiThemeVars.euiColorEmptyShade, + 'editorSuggestWidget.foreground': euiThemeVars.euiTextColor, + 'editorSuggestWidget.highlightForeground': euiThemeVars.euiColorPrimary, + 'editorSuggestWidget.selectedBackground': euiThemeVars.euiColorPrimary, + 'editorSuggestWidget.selectedForeground': euiThemeVars.euiColorEmptyShade, + }, + }; +}; diff --git a/packages/kbn-monaco/src/register_globals.ts b/packages/kbn-monaco/src/register_globals.ts index b4d9c07f78c79..32b8fb0ef2ece 100644 --- a/packages/kbn-monaco/src/register_globals.ts +++ b/packages/kbn-monaco/src/register_globals.ts @@ -11,7 +11,7 @@ import { XJsonLang } from './xjson'; import { PainlessLang } from './painless'; import { SQLLang } from './sql'; import { monaco } from './monaco_imports'; -import { ESQL_THEME_ID, ESQLLang, buildESQlTheme } from './esql'; +import { ESQL_DARK_THEME_ID, ESQL_LIGHT_THEME_ID, ESQLLang, buildESQLTheme } from './esql'; import { YAML_LANG_ID } from './yaml'; import { registerLanguage, registerTheme } from './helpers'; import { ConsoleLang, ConsoleOutputLang, CONSOLE_THEME_ID, buildConsoleTheme } from './console'; @@ -50,7 +50,8 @@ registerLanguage(ConsoleOutputLang); /** * Register custom themes */ -registerTheme(ESQL_THEME_ID, buildESQlTheme()); +registerTheme(ESQL_LIGHT_THEME_ID, buildESQLTheme({ darkMode: false })); +registerTheme(ESQL_DARK_THEME_ID, buildESQLTheme({ darkMode: true })); registerTheme(CONSOLE_THEME_ID, buildConsoleTheme()); registerTheme(CODE_EDITOR_LIGHT_THEME_ID, buildLightTheme()); registerTheme(CODE_EDITOR_DARK_THEME_ID, buildDarkTheme()); From a6dfa6b6f616826d3f17aae8b4be88b0d0afd9ea Mon Sep 17 00:00:00 2001 From: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com> Date: Mon, 18 Nov 2024 21:45:05 +0530 Subject: [PATCH 05/20] [Search][Onboarding] Enable or disable index actions based on user privileges (#197869) ## Summary Disable or show index actions in the search indices index details page based on user privileges. Stack should work as it is, this change is relevant to Serverless search index details page. based on user privilege below buttons are disabled: 1. delete index in index action context menu 2. delete documents in data tab 3. add field in mappings tab 4. edit index settings in setting tab ### Screen recording https://github.com/user-attachments/assets/047f0d9f-b6c3-4665-9cff-a7a4d978b03b ### 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 - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .../components/result/result.tsx | 3 + .../components/result/result_types.ts | 1 + .../components/result/rich_result_header.tsx | 67 ++- .../src/types.ts | 2 + .../details_page/details_page_mappings.tsx | 4 +- .../details_page_mappings_content.tsx | 40 +- .../details_page/details_page_settings.tsx | 4 +- .../details_page_settings_content.tsx | 38 +- .../index_mapping_with_context.tsx | 7 +- .../index_settings_with_context.tsx | 6 +- .../plugins/search_indices/common/routes.ts | 2 +- x-pack/plugins/search_indices/common/types.ts | 3 +- .../components/create_index/create_index.tsx | 13 +- .../create_index/create_index_page.tsx | 4 +- .../index_documents/document_list.tsx | 9 +- .../index_documents/index_documents.tsx | 15 +- .../components/indices/details_page.tsx | 12 +- .../indices/details_page_mappings.tsx | 18 +- .../indices/details_page_menu_item.tsx | 22 +- .../indices/details_page_settings.tsx | 16 +- .../components/shared/create_index_form.tsx | 6 +- .../components/start/elasticsearch_start.tsx | 14 +- .../public/components/start/start_page.tsx | 5 +- .../public/hooks/api/use_user_permissions.ts | 8 +- .../search_indices/server/lib/status.test.ts | 51 ++- .../search_indices/server/lib/status.ts | 10 +- .../search_indices/server/routes/status.ts | 14 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../page_objects/index_management_page.ts | 22 + .../search/search_indices/status.ts | 45 +- .../svl_search_index_detail_page.ts | 64 ++- .../index_management/index_detail.ts | 9 + .../test_suites/search/search_index_detail.ts | 406 +++++++++++------- 35 files changed, 638 insertions(+), 305 deletions(-) diff --git a/packages/kbn-search-index-documents/components/result/result.tsx b/packages/kbn-search-index-documents/components/result/result.tsx index 207a4770b97f2..ff3447229d8ed 100644 --- a/packages/kbn-search-index-documents/components/result/result.tsx +++ b/packages/kbn-search-index-documents/components/result/result.tsx @@ -37,6 +37,7 @@ export interface ResultProps { compactCard?: boolean; onDocumentClick?: () => void; onDocumentDelete?: () => void; + hasDeleteDocumentsPrivilege?: boolean; } export const Result: React.FC = ({ @@ -47,6 +48,7 @@ export const Result: React.FC = ({ showScore = false, onDocumentClick, onDocumentDelete, + hasDeleteDocumentsPrivilege, }) => { const [isExpanded, setIsExpanded] = useState(false); const tooltipText = @@ -97,6 +99,7 @@ export const Result: React.FC = ({ metaData={{ ...metaData, onDocumentDelete, + hasDeleteDocumentsPrivilege, }} /> )} diff --git a/packages/kbn-search-index-documents/components/result/result_types.ts b/packages/kbn-search-index-documents/components/result/result_types.ts index 420951333a05d..fd132b8d30069 100644 --- a/packages/kbn-search-index-documents/components/result/result_types.ts +++ b/packages/kbn-search-index-documents/components/result/result_types.ts @@ -23,6 +23,7 @@ export interface MetaDataProps { title?: string; score?: SearchHit['_score']; showScore?: boolean; + hasDeleteDocumentsPrivilege?: boolean; } export interface FieldProps { diff --git a/packages/kbn-search-index-documents/components/result/rich_result_header.tsx b/packages/kbn-search-index-documents/components/result/rich_result_header.tsx index 98d2ea5a64de0..d4fa39fbe5edf 100644 --- a/packages/kbn-search-index-documents/components/result/rich_result_header.tsx +++ b/packages/kbn-search-index-documents/components/result/rich_result_header.tsx @@ -24,10 +24,12 @@ import { EuiTextColor, EuiTitle, useEuiTheme, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; +import { FormattedMessage } from '@kbn/i18n-react'; import { MetaDataProps } from './result_types'; interface Props { @@ -60,6 +62,7 @@ const MetadataPopover: React.FC = ({ onDocumentDelete, score, showScore = false, + hasDeleteDocumentsPrivilege, }) => { const [popoverIsOpen, setPopoverIsOpen] = useState(false); const closePopover = () => setPopoverIsOpen(false); @@ -85,9 +88,10 @@ const MetadataPopover: React.FC = ({ return ( - {i18n.translate('searchIndexDocuments.result.header.metadata.title', { - defaultMessage: 'Document metadata', - })} + = ({ @@ -118,22 +125,40 @@ const MetadataPopover: React.FC = ({ {onDocumentDelete && ( - ) => { - e.stopPropagation(); - onDocumentDelete(); - closePopover(); - }} - fullWidth + - {i18n.translate('searchIndexDocuments.result.header.metadata.deleteDocument', { - defaultMessage: 'Delete document', - })} - + ) => { + e.stopPropagation(); + onDocumentDelete(); + closePopover(); + }} + fullWidth + > + + + )} diff --git a/x-pack/packages/index-management/index_management_shared_types/src/types.ts b/x-pack/packages/index-management/index_management_shared_types/src/types.ts index ec5c7938d6b4b..02404ddec6213 100644 --- a/x-pack/packages/index-management/index_management_shared_types/src/types.ts +++ b/x-pack/packages/index-management/index_management_shared_types/src/types.ts @@ -79,9 +79,11 @@ export interface Index { export interface IndexMappingProps { index?: Index; showAboutMappings?: boolean; + hasUpdateMappingsPrivilege?: boolean; } export interface IndexSettingProps { indexName: string; + hasUpdateSettingsPrivilege?: boolean; } export interface SendRequestResponse { data: D | null; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx index 77360fd85ad9a..10f5f8be36b85 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings.tsx @@ -19,7 +19,8 @@ import { useLoadIndexMappings } from '../../../../services'; export const DetailsPageMappings: FunctionComponent<{ index?: Index; showAboutMappings?: boolean; -}> = ({ index, showAboutMappings = true }) => { + hasUpdateMappingsPrivilege?: boolean; +}> = ({ index, showAboutMappings = true, hasUpdateMappingsPrivilege }) => { const { isLoading, data, error, resendRequest } = useLoadIndexMappings(index?.name || ''); const [jsonError, setJsonError] = useState(false); @@ -95,6 +96,7 @@ export const DetailsPageMappings: FunctionComponent<{ jsonData={data} showAboutMappings={showAboutMappings} refetchMapping={resendRequest} + hasUpdateMappingsPrivilege={hasUpdateMappingsPrivilege} /> ); }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx index 567d3f782f6f1..e2f9cb68ad90d 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx @@ -22,6 +22,7 @@ import { EuiText, EuiTitle, useGeneratedHtmlId, + EuiToolTip, } from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; @@ -68,7 +69,8 @@ export const DetailsPageMappingsContent: FunctionComponent<{ showAboutMappings: boolean; jsonData: any; refetchMapping: () => void; -}> = ({ index, data, jsonData, refetchMapping, showAboutMappings }) => { + hasUpdateMappingsPrivilege?: boolean; +}> = ({ index, data, jsonData, refetchMapping, showAboutMappings, hasUpdateMappingsPrivilege }) => { const { services: { extensionsService }, core: { @@ -475,18 +477,32 @@ export const DetailsPageMappingsContent: FunctionComponent<{ {!index.hidden && ( {!isAddingFields ? ( - - - + + + + ) : ( updateMappings()} diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings.tsx index e04b4798c4041..0c6f844f2c068 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings.tsx @@ -15,7 +15,8 @@ import { DetailsPageSettingsContent } from './details_page_settings_content'; export const DetailsPageSettings: FunctionComponent<{ indexName: string; -}> = ({ indexName }) => { + hasUpdateSettingsPrivilege?: boolean; +}> = ({ indexName, hasUpdateSettingsPrivilege }) => { const { isLoading, data, error, resendRequest } = useLoadIndexSettings(indexName); if (isLoading) { @@ -76,6 +77,7 @@ export const DetailsPageSettings: FunctionComponent<{ data={data} indexName={indexName} reloadIndexSettings={resendRequest} + hasUpdateSettingsPrivilege={hasUpdateSettingsPrivilege} /> ); }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings_content.tsx index 51ca47ba5c673..95ce72cf59abf 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_settings_content.tsx @@ -19,6 +19,7 @@ import { EuiSwitch, EuiSwitchEvent, EuiText, + EuiToolTip, } from '@elastic/eui'; import { css } from '@emotion/react'; import _ from 'lodash'; @@ -69,12 +70,14 @@ interface Props { data: IndexSettingsResponse; indexName: string; reloadIndexSettings: () => void; + hasUpdateSettingsPrivilege?: boolean; } export const DetailsPageSettingsContent: FunctionComponent = ({ data, indexName, reloadIndexSettings, + hasUpdateSettingsPrivilege, }) => { const [isEditMode, setIsEditMode] = useState(false); const { @@ -184,17 +187,32 @@ export const DetailsPageSettingsContent: FunctionComponent = ({ - + + data-test-subj="indexDetailsSettingsEditModeSwitchToolTip" + > + + } + checked={isEditMode} + onChange={onEditModeChange} + disabled={hasUpdateSettingsPrivilege === false} + /> + + diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_mapping_with_context.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_mapping_with_context.tsx index a341b0fb67813..5b795f57c161b 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_mapping_with_context.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_mapping_with_context.tsx @@ -20,6 +20,7 @@ export const IndexMappingWithContext: React.FC = ( dependencies, index, showAboutMappings, + hasUpdateMappingsPrivilege, }) => { // this normally happens when the index management app is rendered // but if components are embedded elsewhere that setup is skipped, so we have to do it here @@ -42,7 +43,11 @@ export const IndexMappingWithContext: React.FC = ( }; return ( - + ); }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_settings_with_context.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_settings_with_context.tsx index d56c2c46e8ec4..57aba9cda5941 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_settings_with_context.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/with_context_components/index_settings_with_context.tsx @@ -20,6 +20,7 @@ export const IndexSettingsWithContext: React.FC = dependencies, indexName, usageCollection, + hasUpdateSettingsPrivilege, }) => { // this normally happens when the index management app is rendered // but if components are embedded elsewhere that setup is skipped, so we have to do it here @@ -46,7 +47,10 @@ export const IndexSettingsWithContext: React.FC = }; return ( - + ); }; diff --git a/x-pack/plugins/search_indices/common/routes.ts b/x-pack/plugins/search_indices/common/routes.ts index 9ffe1d09d3db5..f527fa676e2a0 100644 --- a/x-pack/plugins/search_indices/common/routes.ts +++ b/x-pack/plugins/search_indices/common/routes.ts @@ -6,7 +6,7 @@ */ export const GET_STATUS_ROUTE = '/internal/search_indices/status'; -export const GET_USER_PRIVILEGES_ROUTE = '/internal/search_indices/start_privileges'; +export const GET_USER_PRIVILEGES_ROUTE = '/internal/search_indices/start_privileges/{indexName}'; export const POST_CREATE_INDEX_ROUTE = '/internal/search_indices/indices/create'; diff --git a/x-pack/plugins/search_indices/common/types.ts b/x-pack/plugins/search_indices/common/types.ts index ef3a46e0301b5..ae5f53a9d073c 100644 --- a/x-pack/plugins/search_indices/common/types.ts +++ b/x-pack/plugins/search_indices/common/types.ts @@ -12,7 +12,8 @@ export interface IndicesStatusResponse { export interface UserStartPrivilegesResponse { privileges: { canCreateApiKeys: boolean; - canCreateIndex: boolean; + canManageIndex: boolean; + canDeleteDocuments: boolean; }; } diff --git a/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx b/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx index d8ce8073c691e..f09ae3856c097 100644 --- a/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx +++ b/x-pack/plugins/search_indices/public/components/create_index/create_index.tsx @@ -7,10 +7,11 @@ import React, { useCallback, useState } from 'react'; -import type { IndicesStatusResponse, UserStartPrivilegesResponse } from '../../../common'; +import type { IndicesStatusResponse } from '../../../common'; import { AnalyticsEvents } from '../../analytics/constants'; import { AvailableLanguages } from '../../code_examples'; +import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; import { useKibana } from '../../hooks/use_kibana'; import { useUsageTracker } from '../../hooks/use_usage_tracker'; import { CreateIndexFormState } from '../../types'; @@ -31,9 +32,8 @@ function initCreateIndexState() { }; } -export interface CreateIndexProps { +interface CreateIndexProps { indicesData?: IndicesStatusResponse; - userPrivileges?: UserStartPrivilegesResponse; } enum CreateIndexViewMode { @@ -41,14 +41,15 @@ enum CreateIndexViewMode { Code = 'code', } -export const CreateIndex = ({ indicesData, userPrivileges }: CreateIndexProps) => { +export const CreateIndex = ({ indicesData }: CreateIndexProps) => { const { application } = useKibana().services; + const [formState, setFormState] = useState(initCreateIndexState); + const { data: userPrivileges } = useUserPrivilegesQuery(formState.defaultIndexName); const [createIndexView, setCreateIndexView] = useState( - userPrivileges?.privileges.canCreateIndex === false + userPrivileges?.privileges.canManageIndex === false ? CreateIndexViewMode.Code : CreateIndexViewMode.UI ); - const [formState, setFormState] = useState(initCreateIndexState); const usageTracker = useUsageTracker(); const onChangeView = useCallback( (id: string) => { diff --git a/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx b/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx index d8601e95760d7..56ee5f49c5339 100644 --- a/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx +++ b/x-pack/plugins/search_indices/public/components/create_index/create_index_page.tsx @@ -13,7 +13,6 @@ import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { useKibana } from '../../hooks/use_kibana'; import { useIndicesStatusQuery } from '../../hooks/api/use_indices_status'; -import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; import { LoadIndicesStatusError } from '../shared/load_indices_status_error'; import { CreateIndex } from './create_index'; @@ -32,7 +31,6 @@ export const CreateIndexPage = () => { isError: hasIndicesStatusFetchError, error: indicesFetchError, } = useIndicesStatusQuery(); - const { data: userPrivileges } = useUserPrivilegesQuery(); const embeddableConsole = useMemo( () => (consolePlugin?.EmbeddableConsole ? : null), @@ -51,7 +49,7 @@ export const CreateIndexPage = () => { {isInitialLoading && } {hasIndicesStatusFetchError && } {!isInitialLoading && !hasIndicesStatusFetchError && ( - + )} {embeddableConsole} diff --git a/x-pack/plugins/search_indices/public/components/index_documents/document_list.tsx b/x-pack/plugins/search_indices/public/components/index_documents/document_list.tsx index e86d1c5ad818a..cf9cce4928d01 100644 --- a/x-pack/plugins/search_indices/public/components/index_documents/document_list.tsx +++ b/x-pack/plugins/search_indices/public/components/index_documents/document_list.tsx @@ -20,9 +20,15 @@ export interface DocumentListProps { indexName: string; docs: SearchHit[]; mappingProperties: Record; + hasDeleteDocumentsPrivilege: boolean; } -export const DocumentList = ({ indexName, docs, mappingProperties }: DocumentListProps) => { +export const DocumentList = ({ + indexName, + docs, + mappingProperties, + hasDeleteDocumentsPrivilege, +}: DocumentListProps) => { const { mutate } = useDeleteDocument(indexName); return ( @@ -39,6 +45,7 @@ export const DocumentList = ({ indexName, docs, mappingProperties }: DocumentLis mutate({ id: doc._id! }); }} compactCard={false} + hasDeleteDocumentsPrivilege={hasDeleteDocumentsPrivilege} /> diff --git a/x-pack/plugins/search_indices/public/components/index_documents/index_documents.tsx b/x-pack/plugins/search_indices/public/components/index_documents/index_documents.tsx index 83595913cece3..5e14275a492f8 100644 --- a/x-pack/plugins/search_indices/public/components/index_documents/index_documents.tsx +++ b/x-pack/plugins/search_indices/public/components/index_documents/index_documents.tsx @@ -5,28 +5,34 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiProgress, EuiSpacer } from '@elastic/eui'; import { useIndexMapping } from '../../hooks/api/use_index_mappings'; import { AddDocumentsCodeExample } from './add_documents_code_example'; import { IndexDocuments as IndexDocumentsType } from '../../hooks/api/use_document_search'; import { DocumentList } from './document_list'; +import type { UserStartPrivilegesResponse } from '../../../common'; interface IndexDocumentsProps { indexName: string; indexDocuments?: IndexDocumentsType; isInitialLoading: boolean; + userPrivileges?: UserStartPrivilegesResponse; } export const IndexDocuments: React.FC = ({ indexName, indexDocuments, isInitialLoading, + userPrivileges, }) => { const { data: mappingData } = useIndexMapping(indexName); const docs = indexDocuments?.results?.data ?? []; const mappingProperties = mappingData?.mappings?.properties ?? {}; + const hasDeleteDocumentsPrivilege: boolean = useMemo(() => { + return userPrivileges?.privileges.canDeleteDocuments ?? false; + }, [userPrivileges]); return ( @@ -38,7 +44,12 @@ export const IndexDocuments: React.FC = ({ )} {docs.length > 0 && ( - + )} diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx index c672bb51493f6..fb09943710dc6 100644 --- a/x-pack/plugins/search_indices/public/components/indices/details_page.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/details_page.tsx @@ -37,6 +37,7 @@ import { SearchIndexDetailsPageMenuItemPopover } from './details_page_menu_item' import { useIndexDocumentSearch } from '../../hooks/api/use_document_search'; import { useUsageTracker } from '../../contexts/usage_tracker_context'; import { AnalyticsEvents } from '../../analytics/constants'; +import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; import { usePageChrome } from '../../hooks/use_page_chrome'; import { IndexManagementBreadcrumbs } from '../shared/breadcrumbs'; @@ -60,6 +61,7 @@ export const SearchIndexDetailsPage = () => { } = useIndexMapping(indexName); const { data: indexDocuments, isInitialLoading: indexDocumentsIsInitialLoading } = useIndexDocumentSearch(indexName); + const { data: userPrivileges } = useUserPrivilegesQuery(indexName); const navigateToPlayground = useCallback(async () => { const playgroundLocator = share.url.locators.get('PLAYGROUND_LOCATOR_ID'); @@ -97,6 +99,7 @@ export const SearchIndexDetailsPage = () => { indexName={indexName} indexDocuments={indexDocuments} isInitialLoading={indexDocumentsIsInitialLoading} + userPrivileges={userPrivileges} /> ), 'data-test-subj': `${SearchIndexDetailsTabs.DATA}Tab`, @@ -106,7 +109,7 @@ export const SearchIndexDetailsPage = () => { name: i18n.translate('xpack.searchIndices.mappingsTabLabel', { defaultMessage: 'Mappings', }), - content: , + content: , 'data-test-subj': `${SearchIndexDetailsTabs.MAPPINGS}Tab`, }, { @@ -114,11 +117,13 @@ export const SearchIndexDetailsPage = () => { name: i18n.translate('xpack.searchIndices.settingsTabLabel', { defaultMessage: 'Settings', }), - content: , + content: ( + + ), 'data-test-subj': `${SearchIndexDetailsTabs.SETTINGS}Tab`, }, ]; - }, [index, indexName, indexDocuments, indexDocumentsIsInitialLoading]); + }, [index, indexName, indexDocuments, indexDocumentsIsInitialLoading, userPrivileges]); const [selectedTab, setSelectedTab] = useState(detailsPageTabs[0]); useEffect(() => { @@ -256,6 +261,7 @@ export const SearchIndexDetailsPage = () => { , diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx index c90d5cad94c83..4ce415b5aba3c 100644 --- a/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/details_page_mappings.tsx @@ -10,10 +10,16 @@ import { Index } from '@kbn/index-management-shared-types'; import React from 'react'; import { useMemo } from 'react'; import { useKibana } from '../../hooks/use_kibana'; +import type { UserStartPrivilegesResponse } from '../../../common'; + export interface SearchIndexDetailsMappingsProps { index?: Index; + userPrivileges?: UserStartPrivilegesResponse; } -export const SearchIndexDetailsMappings = ({ index }: SearchIndexDetailsMappingsProps) => { +export const SearchIndexDetailsMappings = ({ + index, + userPrivileges, +}: SearchIndexDetailsMappingsProps) => { const { indexManagement, history } = useKibana().services; const IndexMappingComponent = useMemo( @@ -21,10 +27,18 @@ export const SearchIndexDetailsMappings = ({ index }: SearchIndexDetailsMappings [indexManagement, history] ); + const hasUpdateMappingsPrivilege = useMemo(() => { + return userPrivileges?.privileges.canManageIndex === true; + }, [userPrivileges]); + return ( <> - + ); }; diff --git a/x-pack/plugins/search_indices/public/components/indices/details_page_menu_item.tsx b/x-pack/plugins/search_indices/public/components/indices/details_page_menu_item.tsx index df45cdab7fba7..9e059660b01ab 100644 --- a/x-pack/plugins/search_indices/public/components/indices/details_page_menu_item.tsx +++ b/x-pack/plugins/search_indices/public/components/indices/details_page_menu_item.tsx @@ -14,21 +14,27 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { ReactElement, useState } from 'react'; +import React, { ReactElement, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { useKibana } from '../../hooks/use_kibana'; +import type { UserStartPrivilegesResponse } from '../../../common'; interface SearchIndexDetailsPageMenuItemPopoverProps { handleDeleteIndexModal: () => void; showApiReference: boolean; + userPrivileges?: UserStartPrivilegesResponse; } export const SearchIndexDetailsPageMenuItemPopover = ({ showApiReference = false, handleDeleteIndexModal, + userPrivileges, }: SearchIndexDetailsPageMenuItemPopoverProps) => { const [showMoreOptions, setShowMoreOptions] = useState(false); const { docLinks } = useKibana().services; + const canManageIndex = useMemo(() => { + return userPrivileges?.privileges.canManageIndex === true; + }, [userPrivileges]); const contextMenuItems = [ showApiReference && ( } + icon={} size="s" onClick={handleDeleteIndexModal} data-test-subj="moreOptionsDeleteIndex" - color="danger" + toolTipContent={ + !canManageIndex + ? i18n.translate('xpack.searchIndices.moreOptions.deleteIndex.permissionToolTip', { + defaultMessage: 'You do not have permission to delete an index', + }) + : undefined + } + toolTipProps={{ 'data-test-subj': 'moreOptionsDeleteIndexTooltip' }} + disabled={!canManageIndex} > - + { +export const SearchIndexDetailsSettings = ({ + indexName, + userPrivileges, +}: SearchIndexDetailsSettingsProps) => { const { indexManagement, history } = useKibana().services; + const hasUpdateSettingsPrivilege = useMemo(() => { + return userPrivileges?.privileges.canManageIndex === true; + }, [userPrivileges]); + const IndexSettingsComponent = useMemo( () => indexManagement.getIndexSettingsComponent({ history }), [indexManagement, history] @@ -24,7 +33,10 @@ export const SearchIndexDetailsSettings = ({ indexName }: SearchIndexDetailsSett return ( <> - + ); }; diff --git a/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx b/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx index ba2f83cb273da..56c8be57a04d3 100644 --- a/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx +++ b/x-pack/plugins/search_indices/public/components/shared/create_index_form.tsx @@ -73,7 +73,7 @@ export const CreateIndexForm = ({ name="indexName" value={indexName} isInvalid={indexNameHasError} - disabled={userPrivileges?.privileges?.canCreateIndex === false} + disabled={userPrivileges?.privileges?.canManageIndex === false} onChange={onIndexNameChange} placeholder={i18n.translate('xpack.searchIndices.shared.createIndex.name.placeholder', { defaultMessage: 'Enter a name for your index', @@ -85,7 +85,7 @@ export const CreateIndexForm = ({ {i18n.translate('xpack.searchIndices.shared.createIndex.permissionTooltip', { defaultMessage: 'You do not have permission to create an index.', @@ -101,7 +101,7 @@ export const CreateIndexForm = ({ iconType="sparkles" data-test-subj="createIndexBtn" disabled={ - userPrivileges?.privileges?.canCreateIndex === false || + userPrivileges?.privileges?.canManageIndex === false || indexNameHasError || isLoading } diff --git a/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx b/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx index 3f3063ddb150e..7b525250ff493 100644 --- a/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx +++ b/x-pack/plugins/search_indices/public/components/start/elasticsearch_start.tsx @@ -8,7 +8,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import type { IndicesStatusResponse, UserStartPrivilegesResponse } from '../../../common'; +import type { IndicesStatusResponse } from '../../../common'; import { AnalyticsEvents } from '../../analytics/constants'; import { AvailableLanguages } from '../../code_examples'; @@ -22,6 +22,7 @@ import { CreateIndexFormState, CreateIndexViewMode } from '../../types'; import { CreateIndexPanel } from '../shared/create_index_panel'; import { useKibana } from '../../hooks/use_kibana'; +import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; function initCreateIndexState(): CreateIndexFormState { const defaultIndexName = generateRandomIndexName(); @@ -34,17 +35,18 @@ function initCreateIndexState(): CreateIndexFormState { export interface ElasticsearchStartProps { indicesData?: IndicesStatusResponse; - userPrivileges?: UserStartPrivilegesResponse; } -export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps) => { +export const ElasticsearchStart: React.FC = () => { const { application } = useKibana().services; + const [formState, setFormState] = useState(initCreateIndexState); + const { data: userPrivileges } = useUserPrivilegesQuery(formState.defaultIndexName); + const [createIndexView, setCreateIndexViewMode] = useState( - userPrivileges?.privileges.canCreateIndex === false + userPrivileges?.privileges.canManageIndex === false ? CreateIndexViewMode.Code : CreateIndexViewMode.UI ); - const [formState, setFormState] = useState(initCreateIndexState); const usageTracker = useUsageTracker(); useEffect(() => { @@ -52,7 +54,7 @@ export const ElasticsearchStart = ({ userPrivileges }: ElasticsearchStartProps) }, [usageTracker]); useEffect(() => { if (userPrivileges === undefined) return; - if (userPrivileges.privileges.canCreateIndex === false) { + if (userPrivileges.privileges.canManageIndex === false) { setCreateIndexViewMode(CreateIndexViewMode.Code); } }, [userPrivileges]); diff --git a/x-pack/plugins/search_indices/public/components/start/start_page.tsx b/x-pack/plugins/search_indices/public/components/start/start_page.tsx index 4dabec2e5fa98..b21eb82c8dcbc 100644 --- a/x-pack/plugins/search_indices/public/components/start/start_page.tsx +++ b/x-pack/plugins/search_indices/public/components/start/start_page.tsx @@ -13,7 +13,6 @@ import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { useKibana } from '../../hooks/use_kibana'; import { useIndicesStatusQuery } from '../../hooks/api/use_indices_status'; -import { useUserPrivilegesQuery } from '../../hooks/api/use_user_permissions'; import { useIndicesRedirect } from './hooks/use_indices_redirect'; import { ElasticsearchStart } from './elasticsearch_start'; @@ -33,7 +32,7 @@ export const ElasticsearchStartPage = () => { isError: hasIndicesStatusFetchError, error: indicesFetchError, } = useIndicesStatusQuery(); - const { data: userPrivileges } = useUserPrivilegesQuery(); + usePageChrome(PageTitle, [...IndexManagementBreadcrumbs, { text: PageTitle }]); const embeddableConsole = useMemo( @@ -53,7 +52,7 @@ export const ElasticsearchStartPage = () => { {isInitialLoading && } {hasIndicesStatusFetchError && } {!isInitialLoading && !hasIndicesStatusFetchError && ( - + )} {embeddableConsole} diff --git a/x-pack/plugins/search_indices/public/hooks/api/use_user_permissions.ts b/x-pack/plugins/search_indices/public/hooks/api/use_user_permissions.ts index d3f4f34887157..ca5cbd10468e9 100644 --- a/x-pack/plugins/search_indices/public/hooks/api/use_user_permissions.ts +++ b/x-pack/plugins/search_indices/public/hooks/api/use_user_permissions.ts @@ -7,16 +7,18 @@ import { useQuery } from '@tanstack/react-query'; -import { GET_USER_PRIVILEGES_ROUTE } from '../../../common/routes'; import type { UserStartPrivilegesResponse } from '../../../common/types'; import { QueryKeys } from '../../constants'; import { useKibana } from '../use_kibana'; -export const useUserPrivilegesQuery = () => { +export const useUserPrivilegesQuery = (indexName: string) => { const { http } = useKibana().services; return useQuery({ queryKey: [QueryKeys.FetchUserStartPrivileges], - queryFn: () => http.get(GET_USER_PRIVILEGES_ROUTE), + queryFn: () => + http.get( + `/internal/search_indices/start_privileges/${indexName}` + ), }); }; diff --git a/x-pack/plugins/search_indices/server/lib/status.test.ts b/x-pack/plugins/search_indices/server/lib/status.test.ts index ff5a8fc1eadd5..bf2250fc8707e 100644 --- a/x-pack/plugins/search_indices/server/lib/status.test.ts +++ b/x-pack/plugins/search_indices/server/lib/status.test.ts @@ -116,6 +116,7 @@ describe('status api lib', function () { }); describe('fetchUserStartPrivileges', function () { + const testIndexName = 'search-zbd1'; it('should return privileges true', async () => { const result: SecurityHasPrivilegesResponse = { application: {}, @@ -124,17 +125,20 @@ describe('status api lib', function () { }, has_all_requested: true, index: { - 'test-index-name': { - create_index: true, + [testIndexName]: { + delete: true, + manage: true, }, }, username: 'unit-test', }; + mockClient.security.hasPrivileges.mockResolvedValue(result); - await expect(fetchUserStartPrivileges(client, logger)).resolves.toEqual({ + await expect(fetchUserStartPrivileges(client, logger, testIndexName)).resolves.toEqual({ privileges: { - canCreateIndex: true, + canManageIndex: true, + canDeleteDocuments: true, canCreateApiKeys: true, }, }); @@ -144,8 +148,8 @@ describe('status api lib', function () { cluster: ['manage_api_key'], index: [ { - names: ['test-index-name'], - privileges: ['create_index'], + names: [testIndexName], + privileges: ['manage', 'delete'], }, ], }); @@ -158,17 +162,19 @@ describe('status api lib', function () { }, has_all_requested: false, index: { - 'test-index-name': { - create_index: false, + [testIndexName]: { + manage: false, + delete: false, }, }, username: 'unit-test', }; mockClient.security.hasPrivileges.mockResolvedValue(result); - await expect(fetchUserStartPrivileges(client, logger)).resolves.toEqual({ + await expect(fetchUserStartPrivileges(client, logger, testIndexName)).resolves.toEqual({ privileges: { - canCreateIndex: false, + canManageIndex: false, + canDeleteDocuments: false, canCreateApiKeys: false, }, }); @@ -181,17 +187,19 @@ describe('status api lib', function () { }, has_all_requested: false, index: { - 'test-index-name': { - create_index: true, + [testIndexName]: { + manage: true, + delete: true, }, }, username: 'unit-test', }; mockClient.security.hasPrivileges.mockResolvedValue(result); - await expect(fetchUserStartPrivileges(client, logger)).resolves.toEqual({ + await expect(fetchUserStartPrivileges(client, logger, testIndexName)).resolves.toEqual({ privileges: { - canCreateIndex: true, + canManageIndex: true, + canDeleteDocuments: true, canCreateApiKeys: false, }, }); @@ -202,17 +210,19 @@ describe('status api lib', function () { cluster: {}, has_all_requested: true, index: { - 'test-index-name': { - create_index: true, + [testIndexName]: { + manage: true, + delete: false, }, }, username: 'unit-test', }; mockClient.security.hasPrivileges.mockResolvedValue(result); - await expect(fetchUserStartPrivileges(client, logger)).resolves.toEqual({ + await expect(fetchUserStartPrivileges(client, logger, testIndexName)).resolves.toEqual({ privileges: { - canCreateIndex: true, + canManageIndex: true, + canDeleteDocuments: false, canCreateApiKeys: false, }, }); @@ -220,9 +230,10 @@ describe('status api lib', function () { it('should default privileges on exceptions', async () => { mockClient.security.hasPrivileges.mockRejectedValue(new Error('Boom!!')); - await expect(fetchUserStartPrivileges(client, logger)).resolves.toEqual({ + await expect(fetchUserStartPrivileges(client, logger, testIndexName)).resolves.toEqual({ privileges: { - canCreateIndex: false, + canManageIndex: false, + canDeleteDocuments: false, canCreateApiKeys: false, }, }); diff --git a/x-pack/plugins/search_indices/server/lib/status.ts b/x-pack/plugins/search_indices/server/lib/status.ts index 752e897ab1707..44ee6cf59abd3 100644 --- a/x-pack/plugins/search_indices/server/lib/status.ts +++ b/x-pack/plugins/search_indices/server/lib/status.ts @@ -38,7 +38,7 @@ export async function fetchIndicesStatus( export async function fetchUserStartPrivileges( client: ElasticsearchClient, logger: Logger, - indexName: string = 'test-index-name' + indexName: string ): Promise { try { const securityCheck = await client.security.hasPrivileges({ @@ -46,14 +46,15 @@ export async function fetchUserStartPrivileges( index: [ { names: [indexName], - privileges: ['create_index'], + privileges: ['manage', 'delete'], }, ], }); return { privileges: { - canCreateIndex: securityCheck?.index?.[indexName]?.create_index ?? false, + canManageIndex: securityCheck?.index?.[indexName]?.manage ?? false, + canDeleteDocuments: securityCheck?.index?.[indexName]?.delete ?? false, canCreateApiKeys: securityCheck?.cluster?.manage_api_key ?? false, }, }; @@ -62,7 +63,8 @@ export async function fetchUserStartPrivileges( logger.error(e); return { privileges: { - canCreateIndex: false, + canManageIndex: false, + canDeleteDocuments: false, canCreateApiKeys: false, }, }; diff --git a/x-pack/plugins/search_indices/server/routes/status.ts b/x-pack/plugins/search_indices/server/routes/status.ts index b135499634487..3ed068780f7d8 100644 --- a/x-pack/plugins/search_indices/server/routes/status.ts +++ b/x-pack/plugins/search_indices/server/routes/status.ts @@ -8,6 +8,7 @@ import type { IRouter } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; +import { schema } from '@kbn/config-schema'; import { GET_STATUS_ROUTE, GET_USER_PRIVILEGES_ROUTE } from '../../common/routes'; import { fetchIndicesStatus, fetchUserStartPrivileges } from '../lib/status'; @@ -35,15 +36,22 @@ export function registerStatusRoutes(router: IRouter, logger: Logger) { router.get( { path: GET_USER_PRIVILEGES_ROUTE, - validate: {}, + validate: { + params: schema.object({ + indexName: schema.string(), + }), + }, options: { access: 'internal', }, }, - async (context, _request, response) => { + async (context, request, response) => { const core = await context.core; const client = core.elasticsearch.client.asCurrentUser; - const body = await fetchUserStartPrivileges(client, logger); + + const { indexName } = request.params; + + const body = await fetchUserStartPrivileges(client, logger, indexName); return response.ok({ body, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index c9d88a7c0f8ed..fef8997584f80 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -7471,7 +7471,6 @@ "searchIndexDocuments.result.expandTooltip.showMore": "Afficher {amount} champs en plus", "searchIndexDocuments.result.header.metadata.deleteDocument": "Supprimer le document", "searchIndexDocuments.result.header.metadata.icon.ariaLabel": "Métadonnées pour le document : {id}", - "searchIndexDocuments.result.header.metadata.score": "Score", "searchIndexDocuments.result.header.metadata.title": "Métadonnées du document", "searchIndexDocuments.result.title.id": "ID de document : {id}", "searchIndexDocuments.result.value.denseVector.copy": "Copier le vecteur", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fffed2d59a462..78eec61c64215 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7463,7 +7463,6 @@ "searchIndexDocuments.result.expandTooltip.showMore": "表示するフィールド数を{amount}個増やす", "searchIndexDocuments.result.header.metadata.deleteDocument": "ドキュメントを削除", "searchIndexDocuments.result.header.metadata.icon.ariaLabel": "ドキュメント{id}のメタデータ", - "searchIndexDocuments.result.header.metadata.score": "スコア", "searchIndexDocuments.result.header.metadata.title": "ドキュメントメタデータ", "searchIndexDocuments.result.title.id": "ドキュメントID:{id}", "searchIndexDocuments.result.value.denseVector.copy": "ベクトルをコピー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4d8de21af735a..d1f0a075b2d21 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7307,7 +7307,6 @@ "searchIndexDocuments.result.expandTooltip.showMore": "显示多于 {amount} 个字段", "searchIndexDocuments.result.header.metadata.deleteDocument": "删除文档", "searchIndexDocuments.result.header.metadata.icon.ariaLabel": "以下文档的元数据:{id}", - "searchIndexDocuments.result.header.metadata.score": "分数", "searchIndexDocuments.result.header.metadata.title": "文档元数据", "searchIndexDocuments.result.title.id": "文档 ID:{id}", "searchIndexDocuments.result.value.denseVector.copy": "复制向量", diff --git a/x-pack/test/functional/page_objects/index_management_page.ts b/x-pack/test/functional/page_objects/index_management_page.ts index 8053293f98633..e5a2604294675 100644 --- a/x-pack/test/functional/page_objects/index_management_page.ts +++ b/x-pack/test/functional/page_objects/index_management_page.ts @@ -159,6 +159,28 @@ export function IndexManagementPageProvider({ getService }: FtrProviderContext) const url = await browser.getCurrentUrl(); expect(url).to.contain(`tab=${tabId}`); }, + async expectEditSettingsToBeEnabled() { + await testSubjects.existOrFail('indexDetailsSettingsEditModeSwitch', { timeout: 2000 }); + const isEditSettingsButtonDisabled = await testSubjects.isEnabled( + 'indexDetailsSettingsEditModeSwitch' + ); + expect(isEditSettingsButtonDisabled).to.be(true); + }, + async expectIndexDetailsMappingsAddFieldToBeEnabled() { + await testSubjects.existOrFail('indexDetailsMappingsAddField'); + const isMappingsFieldEnabled = await testSubjects.isEnabled('indexDetailsMappingsAddField'); + expect(isMappingsFieldEnabled).to.be(true); + }, + async expectTabsExists() { + await testSubjects.existOrFail('indexDetailsTab-mappings', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsTab-overview', { timeout: 2000 }); + await testSubjects.existOrFail('indexDetailsTab-settings', { timeout: 2000 }); + }, + async changeTab( + tab: 'indexDetailsTab-mappings' | 'indexDetailsTab-overview' | 'indexDetailsTab-settings' + ) { + await testSubjects.click(tab); + }, }, async clickCreateIndexButton() { await testSubjects.click('createIndexButton'); diff --git a/x-pack/test_serverless/api_integration/test_suites/search/search_indices/status.ts b/x-pack/test_serverless/api_integration/test_suites/search/search_indices/status.ts index 33a2a438016b9..e92cc62296849 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/search_indices/status.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/search_indices/status.ts @@ -13,6 +13,7 @@ export default function ({ getService }: FtrProviderContext) { const roleScopedSupertest = getService('roleScopedSupertest'); let supertestDeveloperWithCookieCredentials: SupertestWithRoleScopeType; let supertestViewerWithCookieCredentials: SupertestWithRoleScopeType; + const testIndexName = 'search-test-index'; describe('search_indices Status APIs', function () { describe('indices status', function () { @@ -37,37 +38,41 @@ export default function ({ getService }: FtrProviderContext) { describe('developer', function () { it('returns expected privileges', async () => { const { body } = await supertestDeveloperWithCookieCredentials - .get('/internal/search_indices/start_privileges') + .get(`/internal/search_indices/start_privileges/${testIndexName}`) .expect(200); expect(body).toEqual({ privileges: { canCreateApiKeys: true, - canCreateIndex: true, + canDeleteDocuments: true, + canManageIndex: true, }, }); }); }); - describe('viewer', function () { - before(async () => { - supertestViewerWithCookieCredentials = - await roleScopedSupertest.getSupertestWithRoleScope('viewer', { - useCookieHeader: true, - withInternalHeaders: true, - }); - }); + }); + describe('viewer', function () { + before(async () => { + supertestViewerWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( + 'viewer', + { + useCookieHeader: true, + withInternalHeaders: true, + } + ); + }); - it('returns expected privileges', async () => { - const { body } = await supertestViewerWithCookieCredentials - .get('/internal/search_indices/start_privileges') - .expect(200); + it('returns expected privileges', async () => { + const { body } = await supertestViewerWithCookieCredentials + .get(`/internal/search_indices/start_privileges/${testIndexName}`) + .expect(200); - expect(body).toEqual({ - privileges: { - canCreateApiKeys: false, - canCreateIndex: false, - }, - }); + expect(body).toEqual({ + privileges: { + canCreateApiKeys: false, + canDeleteDocuments: false, + canManageIndex: false, + }, }); }); }); diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts index 277b4d2c7ada2..0609b2bec4aed 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts @@ -100,6 +100,18 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont async expectAPIReferenceDocLinkMissingInMoreOptions() { await testSubjects.missingOrFail('moreOptionsApiReference', { timeout: 2000 }); }, + async expectDeleteIndexButtonToBeDisabled() { + await testSubjects.existOrFail('moreOptionsDeleteIndex'); + const deleteIndexButton = await testSubjects.isEnabled('moreOptionsDeleteIndex'); + expect(deleteIndexButton).to.be(false); + await testSubjects.moveMouseTo('moreOptionsDeleteIndex'); + await testSubjects.existOrFail('moreOptionsDeleteIndexTooltip'); + }, + async expectDeleteIndexButtonToBeEnabled() { + await testSubjects.existOrFail('moreOptionsDeleteIndex'); + const deleteIndexButton = await testSubjects.isEnabled('moreOptionsDeleteIndex'); + expect(deleteIndexButton).to.be(true); + }, async expectDeleteIndexButtonExistsInMoreOptions() { await testSubjects.existOrFail('moreOptionsDeleteIndex'); }, @@ -132,11 +144,11 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont await testSubjects.click('reloadButton', 2000); }); }, - async expectWithDataTabsExists() { + async expectTabsExists() { await testSubjects.existOrFail('mappingsTab', { timeout: 2000 }); await testSubjects.existOrFail('dataTab', { timeout: 2000 }); }, - async withDataChangeTabs(tab: 'dataTab' | 'mappingsTab' | 'settingsTab') { + async changeTab(tab: 'dataTab' | 'mappingsTab' | 'settingsTab') { await testSubjects.click(tab); }, async expectUrlShouldChangeTo(tab: 'data' | 'mappings' | 'settings') { @@ -148,6 +160,22 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont async expectSettingsComponentIsVisible() { await testSubjects.existOrFail('indexDetailsSettingsEditModeSwitch', { timeout: 2000 }); }, + async expectEditSettingsIsDisabled() { + await testSubjects.existOrFail('indexDetailsSettingsEditModeSwitch', { timeout: 2000 }); + const isEditSettingsButtonDisabled = await testSubjects.isEnabled( + 'indexDetailsSettingsEditModeSwitch' + ); + expect(isEditSettingsButtonDisabled).to.be(false); + await testSubjects.moveMouseTo('indexDetailsSettingsEditModeSwitch'); + await testSubjects.existOrFail('indexDetailsSettingsEditModeSwitchToolTip'); + }, + async expectEditSettingsToBeEnabled() { + await testSubjects.existOrFail('indexDetailsSettingsEditModeSwitch', { timeout: 2000 }); + const isEditSettingsButtonDisabled = await testSubjects.isEnabled( + 'indexDetailsSettingsEditModeSwitch' + ); + expect(isEditSettingsButtonDisabled).to.be(true); + }, async expectSelectedLanguage(language: string) { await testSubjects.existOrFail('codeExampleLanguageSelect'); expect( @@ -186,12 +214,28 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont await testSubjects.existOrFail('deleteDocumentButton'); await testSubjects.click('deleteDocumentButton'); }, - async expectDeleteDocumentActionNotVisible() { await testSubjects.existOrFail('documentMetadataButton'); await testSubjects.click('documentMetadataButton'); await testSubjects.missingOrFail('deleteDocumentButton'); }, + async expectDeleteDocumentActionIsDisabled() { + await testSubjects.existOrFail('documentMetadataButton'); + await testSubjects.click('documentMetadataButton'); + await testSubjects.existOrFail('deleteDocumentButton'); + const isDeleteDocumentEnabled = await testSubjects.isEnabled('deleteDocumentButton'); + expect(isDeleteDocumentEnabled).to.be(false); + await testSubjects.moveMouseTo('deleteDocumentButton'); + await testSubjects.existOrFail('deleteDocumentButtonToolTip'); + }, + async expectDeleteDocumentActionToBeEnabled() { + await testSubjects.existOrFail('documentMetadataButton'); + await testSubjects.click('documentMetadataButton'); + await testSubjects.existOrFail('deleteDocumentButton'); + const isDeleteDocumentEnabled = await testSubjects.isEnabled('deleteDocumentButton'); + expect(isDeleteDocumentEnabled).to.be(true); + }, + async openIndicesDetailFromIndexManagementIndicesListTable(indexOfRow: number) { const indexList = await testSubjects.findAll('indexTableIndexNameLink'); await indexList[indexOfRow].click(); @@ -219,5 +263,19 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont } } }, + + async expectAddFieldToBeDisabled() { + await testSubjects.existOrFail('indexDetailsMappingsAddField'); + const isMappingsFieldEnabled = await testSubjects.isEnabled('indexDetailsMappingsAddField'); + expect(isMappingsFieldEnabled).to.be(false); + await testSubjects.moveMouseTo('indexDetailsMappingsAddField'); + await testSubjects.existOrFail('indexDetailsMappingsAddFieldTooltip'); + }, + + async expectAddFieldToBeEnabled() { + await testSubjects.existOrFail('indexDetailsMappingsAddField'); + const isMappingsFieldEnabled = await testSubjects.isEnabled('indexDetailsMappingsAddField'); + expect(isMappingsFieldEnabled).to.be(true); + }, }; } diff --git a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts index be3b683d9903a..7330a5d162240 100644 --- a/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts +++ b/x-pack/test_serverless/functional/test_suites/common/management/index_management/index_detail.ts @@ -38,6 +38,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('index with no documents', async () => { await pageObjects.indexManagement.indexDetailsPage.openIndexDetailsPage(0); await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsPageIsLoaded(); + await pageObjects.indexManagement.indexDetailsPage.expectTabsExists(); + }); + it('can add mappings', async () => { + await pageObjects.indexManagement.indexDetailsPage.changeTab('indexDetailsTab-mappings'); + await pageObjects.indexManagement.indexDetailsPage.expectIndexDetailsMappingsAddFieldToBeEnabled(); + }); + it('can edit settings', async () => { + await pageObjects.indexManagement.indexDetailsPage.changeTab('indexDetailsTab-settings'); + await pageObjects.indexManagement.indexDetailsPage.expectEditSettingsToBeEnabled(); }); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts index 0070ce7e2cb43..5aa2627a3cdf4 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts @@ -24,210 +24,286 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const esDeleteAllIndices = getService('esDeleteAllIndices'); const indexName = 'test-my-index'; - describe('Search index detail page', function () { - before(async () => { - await pageObjects.svlCommonPage.loginWithRole('developer'); - await pageObjects.svlApiKeys.deleteAPIKeys(); - }); - after(async () => { - await esDeleteAllIndices(indexName); - }); - - describe('index details page overview', () => { + describe('index details page - search solution', function () { + describe('developer', function () { before(async () => { - await es.indices.create({ index: indexName }); - await svlSearchNavigation.navigateToIndexDetailPage(indexName); + await pageObjects.svlCommonPage.loginWithRole('developer'); + await pageObjects.svlApiKeys.deleteAPIKeys(); }); after(async () => { await esDeleteAllIndices(indexName); }); - it('can load index detail page', async () => { - await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); - await pageObjects.svlSearchIndexDetailPage.expectSearchIndexDetailsTabsExists(); - await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExists(); - await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkMissingInMoreOptions(); - }); - it('should have embedded dev console', async () => { - await testHasEmbeddedConsole(pageObjects); - }); - it('should have connection details', async () => { - await pageObjects.svlSearchIndexDetailPage.expectConnectionDetails(); - }); - - it.skip('should show api key', async () => { - await pageObjects.svlApiKeys.deleteAPIKeys(); - await svlSearchNavigation.navigateToIndexDetailPage(indexName); - await pageObjects.svlApiKeys.expectAPIKeyAvailable(); - const apiKey = await pageObjects.svlApiKeys.getAPIKeyFromUI(); - await pageObjects.svlSearchIndexDetailPage.expectAPIKeyToBeVisibleInCodeBlock(apiKey); - }); - - it('should have quick stats', async () => { - await pageObjects.svlSearchIndexDetailPage.expectQuickStats(); - await pageObjects.svlSearchIndexDetailPage.expectQuickStatsAIMappings(); - await es.indices.putMapping({ - index: indexName, - body: { - properties: { - my_field: { - type: 'dense_vector', - dims: 3, - }, - }, - }, + describe('search index details page', () => { + before(async () => { + await es.indices.create({ index: indexName }); + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + }); + after(async () => { + await esDeleteAllIndices(indexName); + }); + it('can load index detail page', async () => { + await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + await pageObjects.svlSearchIndexDetailPage.expectSearchIndexDetailsTabsExists(); + await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExists(); + await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkMissingInMoreOptions(); + }); + it('should have embedded dev console', async () => { + await testHasEmbeddedConsole(pageObjects); + }); + it('should have connection details', async () => { + await pageObjects.svlSearchIndexDetailPage.expectConnectionDetails(); }); - await svlSearchNavigation.navigateToIndexDetailPage(indexName); - await pageObjects.svlSearchIndexDetailPage.expectQuickStatsAIMappingsToHaveVectorFields(); - }); - - it('should have breadcrumb navigation', async () => { - await pageObjects.svlSearchIndexDetailPage.expectBreadcrumbNavigationWithIndexName( - indexName - ); - await pageObjects.svlSearchIndexDetailPage.clickOnIndexManagementBreadcrumb(); - await pageObjects.indexManagement.expectToBeOnIndicesManagement(); - await svlSearchNavigation.navigateToIndexDetailPage(indexName); - }); - it('should show code examples for adding documents', async () => { - await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples(); - await pageObjects.svlSearchIndexDetailPage.expectSelectedLanguage('python'); - await pageObjects.svlSearchIndexDetailPage.codeSampleContainsValue( - 'installCodeExample', - 'pip install' - ); - await pageObjects.svlSearchIndexDetailPage.selectCodingLanguage('javascript'); - await pageObjects.svlSearchIndexDetailPage.codeSampleContainsValue( - 'installCodeExample', - 'npm install' - ); - await pageObjects.svlSearchIndexDetailPage.selectCodingLanguage('curl'); - await pageObjects.svlSearchIndexDetailPage.openConsoleCodeExample(); - await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen(); - await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar(); - }); + it.skip('should show api key', async () => { + await pageObjects.svlApiKeys.deleteAPIKeys(); + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + await pageObjects.svlApiKeys.expectAPIKeyAvailable(); + const apiKey = await pageObjects.svlApiKeys.getAPIKeyFromUI(); + await pageObjects.svlSearchIndexDetailPage.expectAPIKeyToBeVisibleInCodeBlock(apiKey); + }); - // FLAKY: https://github.com/elastic/kibana/issues/197144 - describe.skip('With data', () => { - before(async () => { - await es.index({ + it('should have quick stats', async () => { + await pageObjects.svlSearchIndexDetailPage.expectQuickStats(); + await pageObjects.svlSearchIndexDetailPage.expectQuickStatsAIMappings(); + await es.indices.putMapping({ index: indexName, body: { - my_field: [1, 0, 1], + properties: { + my_field: { + type: 'dense_vector', + dims: 3, + }, + }, }, }); await svlSearchNavigation.navigateToIndexDetailPage(indexName); + await pageObjects.svlSearchIndexDetailPage.expectQuickStatsAIMappingsToHaveVectorFields(); }); - it('should have index documents', async () => { - await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments(); - }); - it('menu action item should be replaced with playground', async () => { - await pageObjects.svlSearchIndexDetailPage.expectActionItemReplacedWhenHasDocs(); - }); - it('should have link to API reference doc link in options menu', async () => { - await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); - await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExistsInMoreOptions(); + + it('should have breadcrumb navigation', async () => { + await pageObjects.svlSearchIndexDetailPage.expectBreadcrumbNavigationWithIndexName( + indexName + ); + await pageObjects.svlSearchIndexDetailPage.clickOnIndexManagementBreadcrumb(); + await pageObjects.indexManagement.expectToBeOnIndicesManagement(); + await svlSearchNavigation.navigateToIndexDetailPage(indexName); }); - it('should have one document in quick stats', async () => { - await pageObjects.svlSearchIndexDetailPage.expectQuickStatsToHaveDocumentCount(1); + + it('should show code examples for adding documents', async () => { + await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples(); + await pageObjects.svlSearchIndexDetailPage.expectSelectedLanguage('python'); + await pageObjects.svlSearchIndexDetailPage.codeSampleContainsValue( + 'installCodeExample', + 'pip install' + ); + await pageObjects.svlSearchIndexDetailPage.selectCodingLanguage('javascript'); + await pageObjects.svlSearchIndexDetailPage.codeSampleContainsValue( + 'installCodeExample', + 'npm install' + ); + await pageObjects.svlSearchIndexDetailPage.selectCodingLanguage('curl'); + await pageObjects.svlSearchIndexDetailPage.openConsoleCodeExample(); + await pageObjects.embeddedConsole.expectEmbeddedConsoleToBeOpen(); + await pageObjects.embeddedConsole.clickEmbeddedConsoleControlBar(); }); - it('should have with data tabs', async () => { - await pageObjects.svlSearchIndexDetailPage.expectWithDataTabsExists(); - await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('data'); + + // FLAKY: https://github.com/elastic/kibana/issues/197144 + describe.skip('With data', () => { + before(async () => { + await es.index({ + index: indexName, + body: { + my_field: [1, 0, 1], + }, + }); + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + }); + it('should have index documents', async () => { + await pageObjects.svlSearchIndexDetailPage.expectHasIndexDocuments(); + }); + it('menu action item should be replaced with playground', async () => { + await pageObjects.svlSearchIndexDetailPage.expectActionItemReplacedWhenHasDocs(); + }); + it('should have link to API reference doc link in options menu', async () => { + await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); + await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExistsInMoreOptions(); + }); + it('should have one document in quick stats', async () => { + await pageObjects.svlSearchIndexDetailPage.expectQuickStatsToHaveDocumentCount(1); + }); + it('should have with data tabs', async () => { + await pageObjects.svlSearchIndexDetailPage.expectTabsExists(); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('data'); + }); + it('should be able to change tabs to mappings and mappings is shown', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('mappingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('mappings'); + await pageObjects.svlSearchIndexDetailPage.expectMappingsComponentIsVisible(); + }); + it('should be able to change tabs to settings and settings is shown', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('settingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('settings'); + await pageObjects.svlSearchIndexDetailPage.expectSettingsComponentIsVisible(); + }); + it('should be able to delete document', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('dataTab'); + await pageObjects.svlSearchIndexDetailPage.clickFirstDocumentDeleteAction(); + await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples(); + await pageObjects.svlSearchIndexDetailPage.expectQuickStatsToHaveDocumentCount(0); + }); }); - it('should be able to change tabs to mappings and mappings is shown', async () => { - await pageObjects.svlSearchIndexDetailPage.withDataChangeTabs('mappingsTab'); - await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('mappings'); - await pageObjects.svlSearchIndexDetailPage.expectMappingsComponentIsVisible(); + describe('has index actions enabled', () => { + before(async () => { + await es.index({ + index: indexName, + body: { + my_field: [1, 0, 1], + }, + }); + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + }); + + beforeEach(async () => { + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + }); + + it('delete document button is enabled', async () => { + await pageObjects.svlSearchIndexDetailPage.expectDeleteDocumentActionToBeEnabled(); + }); + it('add field button is enabled', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('mappingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectAddFieldToBeEnabled(); + }); + it('edit settings button is enabled', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('settingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectEditSettingsToBeEnabled(); + }); + it('delete index button is enabled', async () => { + await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsActionButtonExists(); + await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); + await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsOverviewMenuIsShown(); + await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonExistsInMoreOptions(); + await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonToBeEnabled(); + }); }); - it('should be able to change tabs to settings and settings is shown', async () => { - await pageObjects.svlSearchIndexDetailPage.withDataChangeTabs('settingsTab'); - await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('settings'); - await pageObjects.svlSearchIndexDetailPage.expectSettingsComponentIsVisible(); + + describe('page loading error', () => { + before(async () => { + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + await esDeleteAllIndices(indexName); + }); + it('has page load error section', async () => { + await pageObjects.svlSearchIndexDetailPage.expectPageLoadErrorExists(); + await pageObjects.svlSearchIndexDetailPage.expectIndexNotFoundErrorExists(); + }); + it('reload button shows details page again', async () => { + await es.indices.create({ index: indexName }); + await pageObjects.svlSearchIndexDetailPage.clickPageReload(); + await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + }); }); - it('should be able to delete document', async () => { - await pageObjects.svlSearchIndexDetailPage.withDataChangeTabs('dataTab'); - await pageObjects.svlSearchIndexDetailPage.clickFirstDocumentDeleteAction(); - await pageObjects.svlSearchIndexDetailPage.expectAddDocumentCodeExamples(); - await pageObjects.svlSearchIndexDetailPage.expectQuickStatsToHaveDocumentCount(0); + describe('Index more options menu', () => { + before(async () => { + await svlSearchNavigation.navigateToIndexDetailPage(indexName); + }); + it('shows action menu in actions popover', async () => { + await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsActionButtonExists(); + await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); + await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsOverviewMenuIsShown(); + }); + it('should delete index', async () => { + await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonExistsInMoreOptions(); + await pageObjects.svlSearchIndexDetailPage.clickDeleteIndexButton(); + await pageObjects.svlSearchIndexDetailPage.clickConfirmingDeleteIndex(); + }); }); }); - - describe('page loading error', () => { + describe('index management index list page', () => { before(async () => { - await svlSearchNavigation.navigateToIndexDetailPage(indexName); - await esDeleteAllIndices(indexName); - }); - it('has page load error section', async () => { - await pageObjects.svlSearchIndexDetailPage.expectPageLoadErrorExists(); - await pageObjects.svlSearchIndexDetailPage.expectIndexNotFoundErrorExists(); - }); - it('reload button shows details page again', async () => { await es.indices.create({ index: indexName }); - await pageObjects.svlSearchIndexDetailPage.clickPageReload(); - await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + await security.testUser.setRoles(['index_management_user']); }); - }); - describe('Index more options menu', () => { - before(async () => { - await svlSearchNavigation.navigateToIndexDetailPage(indexName); + beforeEach(async () => { + await pageObjects.common.navigateToApp('indexManagement'); + // Navigate to the indices tab + await pageObjects.indexManagement.changeTabs('indicesTab'); + await pageObjects.header.waitUntilLoadingHasFinished(); }); - it('shows action menu in actions popover', async () => { - await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsActionButtonExists(); - await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); - await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsOverviewMenuIsShown(); + after(async () => { + await esDeleteAllIndices(indexName); }); - it('should delete index', async () => { - await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonExistsInMoreOptions(); - await pageObjects.svlSearchIndexDetailPage.clickDeleteIndexButton(); - await pageObjects.svlSearchIndexDetailPage.clickConfirmingDeleteIndex(); + describe('manage index action', () => { + beforeEach(async () => { + await pageObjects.indexManagement.manageIndex(indexName); + await pageObjects.indexManagement.manageIndexContextMenuExists(); + }); + it('navigates to overview tab', async () => { + await pageObjects.indexManagement.changeManageIndexTab('showOverviewIndexMenuButton'); + await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('data'); + }); + + it('navigates to settings tab', async () => { + await pageObjects.indexManagement.changeManageIndexTab('showSettingsIndexMenuButton'); + await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('settings'); + }); + it('navigates to mappings tab', async () => { + await pageObjects.indexManagement.changeManageIndexTab('showMappingsIndexMenuButton'); + await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('mappings'); + }); + }); + describe('can view search index details', function () { + it('renders search index details with no documents', async () => { + await pageObjects.svlSearchIndexDetailPage.openIndicesDetailFromIndexManagementIndicesListTable( + 0 + ); + await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); + await pageObjects.svlSearchIndexDetailPage.expectSearchIndexDetailsTabsExists(); + await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExists(); + }); }); }); }); - describe('index management index details', () => { + + describe('viewer', function () { before(async () => { - await es.indices.create({ index: indexName }); - await security.testUser.setRoles(['index_management_user']); - }); - beforeEach(async () => { - await pageObjects.common.navigateToApp('indexManagement'); - // Navigate to the indices tab - await pageObjects.indexManagement.changeTabs('indicesTab'); - await pageObjects.header.waitUntilLoadingHasFinished(); + await esDeleteAllIndices(indexName); + await es.index({ + index: indexName, + body: { + my_field: [1, 0, 1], + }, + }); }); after(async () => { await esDeleteAllIndices(indexName); }); - describe('manage index action', () => { + describe('search index details page', function () { + before(async () => { + await pageObjects.svlCommonPage.loginAsViewer(); + }); beforeEach(async () => { - await pageObjects.indexManagement.manageIndex(indexName); - await pageObjects.indexManagement.manageIndexContextMenuExists(); + await svlSearchNavigation.navigateToIndexDetailPage(indexName); }); - it('navigates to overview tab', async () => { - await pageObjects.indexManagement.changeManageIndexTab('showOverviewIndexMenuButton'); - await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); - await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('data'); + it('delete document button is disabled', async () => { + await pageObjects.svlSearchIndexDetailPage.expectDeleteDocumentActionIsDisabled(); }); - - it('navigates to settings tab', async () => { - await pageObjects.indexManagement.changeManageIndexTab('showSettingsIndexMenuButton'); - await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); - await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('settings'); + it('add field button is disabled', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('mappingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectAddFieldToBeDisabled(); }); - it('navigates to mappings tab', async () => { - await pageObjects.indexManagement.changeManageIndexTab('showMappingsIndexMenuButton'); - await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); - await pageObjects.svlSearchIndexDetailPage.expectUrlShouldChangeTo('mappings'); + it('edit settings button is disabled', async () => { + await pageObjects.svlSearchIndexDetailPage.changeTab('settingsTab'); + await pageObjects.svlSearchIndexDetailPage.expectEditSettingsIsDisabled(); }); - }); - describe('can view search index details', function () { - it('renders search index details with no documents', async () => { - await pageObjects.svlSearchIndexDetailPage.openIndicesDetailFromIndexManagementIndicesListTable( - 0 - ); - await pageObjects.svlSearchIndexDetailPage.expectIndexDetailPageHeader(); - await pageObjects.svlSearchIndexDetailPage.expectSearchIndexDetailsTabsExists(); - await pageObjects.svlSearchIndexDetailPage.expectAPIReferenceDocLinkExists(); + it('delete index button is disabled', async () => { + await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsActionButtonExists(); + await pageObjects.svlSearchIndexDetailPage.clickMoreOptionsActionsButton(); + await pageObjects.svlSearchIndexDetailPage.expectMoreOptionsOverviewMenuIsShown(); + await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonExistsInMoreOptions(); + await pageObjects.svlSearchIndexDetailPage.expectDeleteIndexButtonToBeDisabled(); }); }); }); From 1e29cf8af1d8a7d60f156e583ada9e62e8bb16c2 Mon Sep 17 00:00:00 2001 From: Tre Date: Mon, 18 Nov 2024 16:17:13 +0000 Subject: [PATCH 06/20] [Ownership] Assign maps test files to presentation team (#200214) ## Summary Assign maps test files to presentation team Contributes to: #192979 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Nick Peihl --- .github/CODEOWNERS | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c2450338f3e45..7b4cd85b0d8d0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1382,6 +1382,17 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /test/plugin_functional/test_suites/panel_actions @elastic/kibana-presentation /x-pack/test/functional/es_archives/canvas/logstash_lens @elastic/kibana-presentation #CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation +/x-pack/test/upgrade/services/maps_upgrade_services.ts @elastic/kibana-presentation +/x-pack/test/stack_functional_integration/apps/maps @elastic/kibana-presentation +/x-pack/test/functional/page_objects/geo_file_upload.ts @elastic/kibana-presentation +/x-pack/test/functional/page_objects/gis_page.ts @elastic/kibana-presentation +/x-pack/test/upgrade/apps/maps @elastic/kibana-presentation +/x-pack/test/api_integration/apis/maps/ @elastic/kibana-presentation +/x-pack/test/functional/apps/maps/ @elastic/kibana-presentation +/x-pack/test/functional/es_archives/maps/ @elastic/kibana-presentation +/x-pack/plugins/stack_alerts/server/rule_types/geo_containment @elastic/kibana-presentation +/x-pack/plugins/stack_alerts/public/rule_types/geo_containment @elastic/kibana-presentation + # Machine Learning /x-pack/test/stack_functional_integration/apps/ml @elastic/ml-ui @@ -1421,15 +1432,6 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai /x-pack/test/functional/services/aiops @elastic/ml-ui /x-pack/test/functional_basic/apps/transform/ @elastic/ml-ui -# Maps -#CC# /x-pack/plugins/maps/ @elastic/kibana-gis -/x-pack/test/api_integration/apis/maps/ @elastic/kibana-gis -/x-pack/test/functional/apps/maps/ @elastic/kibana-gis -/x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis -/x-pack/plugins/stack_alerts/server/rule_types/geo_containment @elastic/kibana-gis -/x-pack/plugins/stack_alerts/public/rule_types/geo_containment @elastic/kibana-gis -#CC# /x-pack/plugins/file_upload @elastic/kibana-gis - # Operations /test/package @elastic/kibana-operations /test/package/roles @elastic/kibana-operations From 5ab39e5a413f426da232723317786321a7d57fc7 Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Mon, 18 Nov 2024 17:23:05 +0100 Subject: [PATCH 07/20] [Security Solution] [Bug] Index Values are not available in dropdown under New Index Enter for Knowledge Base. (#199610) (#199990) ## Summary This PR fixes the issue described here https://github.com/elastic/kibana/issues/199610#issuecomment-2473393109 In short, we do not show all indices with `semantic_text` fields. We only show those which have `semantic_text` fields named `content`. ### Testing notes Add the index with the `semantic_text` field by running this command in dev tools: ``` PUT test_index { "mappings": { "properties": { "attachment": { "properties": { "content": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "content_length": { "type": "long" }, "content_type": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "format": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "language": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } }, "content2": { "type": "semantic_text", "inference_id": "elastic-security-ai-assistant-elser2" } } } } ``` You should see the `test_index` index in the `Knowledge Base > New > Index > Index (dropdown)`. ### Checklist Delete any items that are not applicable to this PR. - [ ] [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: Steph Milovic --- .../get_knowledge_base_indices.test.ts | 74 ++++++++++++++++--- .../get_knowledge_base_indices.ts | 8 +- 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.test.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.test.ts index e7eaa75407248..d5e92cb8d682e 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.test.ts @@ -12,12 +12,15 @@ import { getGetKnowledgeBaseIndicesRequest } from '../../__mocks__/request'; const mockFieldCaps = { indices: [ - '.ds-logs-endpoint.alerts-default-2024.10.31-000001', - '.ds-metrics-endpoint.metadata-default-2024.10.31-000001', - '.internal.alerts-security.alerts-default-000001', + '.ds-.items-default-2024.11.12-000001', + '.ds-.lists-default-2024.11.12-000001', + '.ds-logs-endpoint.alerts-default-2024.11.12-000001', + '.ds-logs-endpoint.events.process-default-2024.11.12-000001', + 'gtr-1', + 'gtr-with-bug', + 'gtr-with-semantic-1', 'metrics-endpoint.metadata_current_default', - 'semantic-index-1', - 'semantic-index-2', + 'search-elastic-security-docs', ], fields: { content: { @@ -27,9 +30,12 @@ const mockFieldCaps = { searchable: false, aggregatable: false, indices: [ - '.ds-logs-endpoint.alerts-default-2024.10.31-000001', - '.ds-metrics-endpoint.metadata-default-2024.10.31-000001', - '.internal.alerts-security.alerts-default-000001', + '.ds-.items-default-2024.11.12-000001', + '.ds-.lists-default-2024.11.12-000001', + '.ds-logs-endpoint.alerts-default-2024.11.12-000001', + '.ds-logs-endpoint.events.process-default-2024.11.12-000001', + 'gtr-1', + 'gtr-with-bug', 'metrics-endpoint.metadata_current_default', ], }, @@ -38,7 +44,55 @@ const mockFieldCaps = { metadata_field: false, searchable: true, aggregatable: false, - indices: ['semantic-index-1', 'semantic-index-2'], + indices: ['gtr-with-semantic-1'], + }, + }, + ai_embeddings: { + unmapped: { + type: 'unmapped', + metadata_field: false, + searchable: false, + aggregatable: false, + indices: [ + '.ds-.items-default-2024.11.12-000001', + '.ds-.lists-default-2024.11.12-000001', + '.ds-logs-endpoint.alerts-default-2024.11.12-000001', + '.ds-logs-endpoint.events.process-default-2024.11.12-000001', + 'gtr-1', + 'gtr-with-semantic-1', + 'metrics-endpoint.metadata_current_default', + ], + }, + semantic_text: { + type: 'semantic_text', + metadata_field: false, + searchable: true, + aggregatable: false, + indices: ['gtr-with-bug', 'search-elastic-security-docs'], + }, + }, + semantic_text: { + unmapped: { + type: 'unmapped', + metadata_field: false, + searchable: false, + aggregatable: false, + indices: [ + '.ds-.items-default-2024.11.12-000001', + '.ds-.lists-default-2024.11.12-000001', + '.ds-logs-endpoint.alerts-default-2024.11.12-000001', + '.ds-logs-endpoint.events.process-default-2024.11.12-000001', + 'gtr-1', + 'gtr-with-semantic-1', + 'metrics-endpoint.metadata_current_default', + ], + }, + semantic_text: { + type: 'semantic_text', + metadata_field: false, + searchable: true, + aggregatable: false, + indices: ['search-elastic-security-docs'], }, }, }, @@ -66,7 +120,7 @@ describe('Get Knowledge Base Status Route', () => { expect(response.status).toEqual(200); expect(response.body).toEqual({ - indices: ['semantic-index-1', 'semantic-index-2'], + indices: ['gtr-with-bug', 'gtr-with-semantic-1', 'search-elastic-security-docs'], }); expect(context.core.elasticsearch.client.asCurrentUser.fieldCaps).toBeCalledWith({ index: '*', diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts index 18191291468de..5106c31d39e7d 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts @@ -53,10 +53,10 @@ export const getKnowledgeBaseIndicesRoute = (router: ElasticAssistantPluginRoute include_unmapped: true, }); - const indices = res.fields.content?.semantic_text?.indices; - if (indices) { - body.indices = Array.isArray(indices) ? indices : [indices]; - } + body.indices = Object.values(res.fields) + .flatMap((value) => value.semantic_text?.indices ?? []) + .filter((value, index, self) => self.indexOf(value) === index) + .sort(); return response.ok({ body }); } catch (err) { From aa0dcdcf0164a916d569ac269e87bb3179c467c2 Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Mon, 18 Nov 2024 17:24:04 +0100 Subject: [PATCH 08/20] [Security GenAI] Fetching Assistant Knowledge Base fails when current user's username contains a : character (#11159) (#200131) ## Summary Original bug: [internal link](https://github.com/elastic/security-team/issues/11159) **This PR fixes the next bug**: When the user is logged in with a username that contains a `:` character, fetching Knowlege Base entries fails with an error. This is preventing customers from viewing their created KB entries. This problem affects ECE customers using the SSO login option. There were a similar bugfix which inspired this one https://github.com/elastic/kibana/pull/181709 There is no easy way to reproduce this but you can try and change the line in question so that the faulty username is used instead of the one passed in. @MadameSheema Do you know a way to login locally with the username that contains a `:` character? As mentioned above this situation is possible with the ECE customers using SSO login. ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../knowledge_base/entries/utils.test.ts | 43 +++++++++++++++++++ .../routes/knowledge_base/entries/utils.ts | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.test.ts diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.test.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.test.ts new file mode 100644 index 0000000000000..e718ff44630c7 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.test.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AuthenticatedUser } from '@kbn/core-security-common'; +import { getKBUserFilter } from './utils'; + +describe('Utils', () => { + describe('getKBUserFilter', () => { + it('should return global filter when user is null', () => { + const filter = getKBUserFilter(null); + expect(filter).toEqual('(NOT users: {name:* OR id:* })'); + }); + + it('should return global filter when `username` and `profile_uid` are undefined', () => { + const filter = getKBUserFilter({} as AuthenticatedUser); + expect(filter).toEqual('(NOT users: {name:* OR id:* })'); + }); + + it('should return global filter when `username` is undefined', () => { + const filter = getKBUserFilter({ profile_uid: 'fake_user_id' } as AuthenticatedUser); + expect(filter).toEqual('(NOT users: {name:* OR id:* } OR users: {id: fake_user_id})'); + }); + + it('should return global filter when `profile_uid` is undefined', () => { + const filter = getKBUserFilter({ username: 'user1' } as AuthenticatedUser); + expect(filter).toEqual('(NOT users: {name:* OR id:* } OR users: {name: "user1"})'); + }); + + it('should return global filter when `username` has semicolon', () => { + const filter = getKBUserFilter({ + username: 'user:1', + profile_uid: 'fake_user_id', + } as AuthenticatedUser); + expect(filter).toEqual( + '(NOT users: {name:* OR id:* } OR (users: {name: "user:1"} OR users: {id: fake_user_id}))' + ); + }); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.ts index 3a548cd812539..0f5a0ab97fb29 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/utils.ts @@ -11,7 +11,7 @@ export const getKBUserFilter = (user: AuthenticatedUser | null) => { // Only return the current users entries and all other global entries (where user[] is empty) const globalFilter = 'NOT users: {name:* OR id:* }'; - const nameFilter = user?.username ? `users: {name: ${user?.username}}` : ''; + const nameFilter = user?.username ? `users: {name: "${user?.username}"}` : ''; const idFilter = user?.profile_uid ? `users: {id: ${user?.profile_uid}}` : ''; const userFilter = user?.username && user?.profile_uid From 1607803f610cbbc97883b78578723fa8400c7b4a Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Mon, 18 Nov 2024 17:27:32 +0100 Subject: [PATCH 09/20] [Inventory] Remove open in Discover button (#200574) Closes #199964 This PR removes the `open in discover` button from the inventory search bar section. | Before | After | | ------- | ----- | | image | image | --- .../inventory/e2e/cypress/e2e/home.cy.ts | 29 ------------- .../components/search_bar/discover_button.tsx | 35 ---------------- .../public/components/search_bar/index.tsx | 42 +++++++------------ .../translations/translations/fr-FR.json | 2 +- .../translations/translations/ja-JP.json | 2 +- .../translations/translations/zh-CN.json | 2 +- 6 files changed, 18 insertions(+), 94 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx diff --git a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts index 17b6cf502280a..c9d341c708965 100644 --- a/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts +++ b/x-pack/plugins/observability_solution/inventory/e2e/cypress/e2e/home.cy.ts @@ -121,35 +121,6 @@ describe('Home page', () => { cy.url().should('include', '/app/metrics/detail/host/server1'); }); - it('Navigates to discover with default filter', () => { - cy.intercept('GET', '/internal/entities/managed/enablement', { - fixture: 'eem_enabled.json', - }).as('getEEMStatus'); - cy.visitKibana('/app/inventory'); - cy.wait('@getEEMStatus'); - cy.contains('Open in discover').click(); - cy.url().should( - 'include', - "query:(language:kuery,query:'entity.definition_id%20:%20builtin*" - ); - }); - - it('Navigates to discover with kuery filter', () => { - cy.intercept('GET', '/internal/entities/managed/enablement', { - fixture: 'eem_enabled.json', - }).as('getEEMStatus'); - cy.visitKibana('/app/inventory'); - cy.wait('@getEEMStatus'); - cy.getByTestSubj('queryInput').type('service.name : foo'); - - cy.contains('Update').click(); - cy.contains('Open in discover').click(); - cy.url().should( - 'include', - "query:'service.name%20:%20foo%20AND%20entity.definition_id%20:%20builtin*'" - ); - }); - it('Navigates to infra when clicking on a container type entity', () => { cy.intercept('GET', '/internal/entities/managed/enablement', { fixture: 'eem_enabled.json', diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx deleted file mode 100644 index 13477d63e5f82..0000000000000 --- a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/discover_button.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiButton } from '@elastic/eui'; -import { DataView } from '@kbn/data-views-plugin/public'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useDiscoverRedirect } from '../../hooks/use_discover_redirect'; - -export function DiscoverButton({ dataView }: { dataView: DataView }) { - const { getDiscoverRedirectUrl } = useDiscoverRedirect(); - - const discoverLink = getDiscoverRedirectUrl(); - - if (!discoverLink) { - return null; - } - - return ( - - {i18n.translate('xpack.inventory.searchBar.discoverButton', { - defaultMessage: 'Open in discover', - })} - - ); -} diff --git a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx index d1ccfd3f358e3..3464c5749dbc3 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/search_bar/index.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import type { SearchBarOwnProps } from '@kbn/unified-search-plugin/public/search_bar'; @@ -14,7 +13,6 @@ import { useKibana } from '../../hooks/use_kibana'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search_context'; import { getKqlFieldsWithFallback } from '../../utils/get_kql_field_names_with_fallback'; import { ControlGroups } from './control_groups'; -import { DiscoverButton } from './discover_button'; export function SearchBar() { const { refreshSubject$, dataView, searchState, onQueryChange } = useUnifiedSearchContext(); @@ -73,30 +71,20 @@ export function SearchBar() { ); return ( - - - } - onQuerySubmit={handleQuerySubmit} - placeholder={i18n.translate('xpack.inventory.searchBar.placeholder', { - defaultMessage: - 'Search for your entities by name or its metadata (e.g. entity.type : service)', - })} - showDatePicker={false} - showFilterBar - showQueryInput - showQueryMenu - /> - - - {dataView ? ( - - - - ) : null} - + } + onQuerySubmit={handleQuerySubmit} + placeholder={i18n.translate('xpack.inventory.searchBar.placeholder', { + defaultMessage: + 'Search for your entities by name or its metadata (e.g. entity.type : service)', + })} + showDatePicker={false} + showFilterBar + showQueryInput + showQueryMenu + /> ); } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index fef8997584f80..49566da1f6b18 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -26205,6 +26205,7 @@ "xpack.inventory.badgeFilterWithPopover.openPopoverBadgeLabel": "Ouvrir la fenêtre contextuelle", "xpack.inventory.data_view.creation_failed": "Une erreur s'est produite lors de la création de la vue de données", "xpack.inventory.eemEnablement.errorTitle": "Erreur lors de l'activation du nouveau modèle d'entité", + "xpack.inventory.entityActions.discoverLink": "Ouvrir dans Discover", "xpack.inventory.entitiesGrid.euiDataGrid.alertsLabel": "Alertes", "xpack.inventory.entitiesGrid.euiDataGrid.alertsTooltip": "Le nombre d'alertes actives", "xpack.inventory.entitiesGrid.euiDataGrid.entityNameLabel": "Nom de l'entité", @@ -26234,7 +26235,6 @@ "xpack.inventory.noEntitiesEmptyState.description": "L'affichage de vos entités peut prendre quelques minutes. Essayez de rafraîchir à nouveau dans une minute ou deux.", "xpack.inventory.noEntitiesEmptyState.learnMore.link": "En savoir plus", "xpack.inventory.noEntitiesEmptyState.title": "Aucune entité disponible", - "xpack.inventory.searchBar.discoverButton": "Ouvrir dans Discover", "xpack.inventory.searchBar.placeholder": "Recherchez vos entités par nom ou par leurs métadonnées (par exemple entity.type : service)", "xpack.inventory.shareLink.shareButtonLabel": "Partager", "xpack.inventory.shareLink.shareToastFailureLabel": "Les URL courtes ne peuvent pas être copiées.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 78eec61c64215..f0cf7c38ac66b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -26177,6 +26177,7 @@ "xpack.inventory.badgeFilterWithPopover.openPopoverBadgeLabel": "ポップオーバーを開く", "xpack.inventory.data_view.creation_failed": "データビューの作成中にエラーが発生しました", "xpack.inventory.eemEnablement.errorTitle": "新しいエンティティモデルの有効化エラー", + "xpack.inventory.entityActions.discoverLink": "Discoverで開く", "xpack.inventory.entitiesGrid.euiDataGrid.alertsLabel": "アラート", "xpack.inventory.entitiesGrid.euiDataGrid.alertsTooltip": "アクティブなアラートの件数", "xpack.inventory.entitiesGrid.euiDataGrid.entityNameLabel": "エンティティ名", @@ -26206,7 +26207,6 @@ "xpack.inventory.noEntitiesEmptyState.description": "エンティティが表示されるまで数分かかる場合があります。1〜2分後に更新してください。", "xpack.inventory.noEntitiesEmptyState.learnMore.link": "詳細", "xpack.inventory.noEntitiesEmptyState.title": "エンティティがありません", - "xpack.inventory.searchBar.discoverButton": "Discoverで開く", "xpack.inventory.searchBar.placeholder": "エンティティを名前またはメタデータ(例:entity.type : service)で検索します。", "xpack.inventory.shareLink.shareButtonLabel": "共有", "xpack.inventory.shareLink.shareToastFailureLabel": "短縮URLをコピーできません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d1f0a075b2d21..c69512018d0f4 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25703,6 +25703,7 @@ "xpack.inventory.badgeFilterWithPopover.openPopoverBadgeLabel": "打开弹出框", "xpack.inventory.data_view.creation_failed": "创建数据视图时出错", "xpack.inventory.eemEnablement.errorTitle": "启用新实体模型时出错", + "xpack.inventory.entityActions.discoverLink": "在 Discover 中打开", "xpack.inventory.entitiesGrid.euiDataGrid.alertsLabel": "告警", "xpack.inventory.entitiesGrid.euiDataGrid.alertsTooltip": "活动告警计数", "xpack.inventory.entitiesGrid.euiDataGrid.entityNameLabel": "实体名称", @@ -25732,7 +25733,6 @@ "xpack.inventory.noEntitiesEmptyState.description": "您的实体可能需要数分钟才能显示。请尝试在一或两分钟后刷新。", "xpack.inventory.noEntitiesEmptyState.learnMore.link": "了解详情", "xpack.inventory.noEntitiesEmptyState.title": "无可用实体", - "xpack.inventory.searchBar.discoverButton": "在 Discover 中打开", "xpack.inventory.searchBar.placeholder": "按名称或其元数据(例如,entity.type:服务)搜索您的实体", "xpack.inventory.shareLink.shareButtonLabel": "共享", "xpack.inventory.shareLink.shareToastFailureLabel": "无法复制短 URL。", From d804b38ea4211f3fb4f84b8594ee9d14f071959f Mon Sep 17 00:00:00 2001 From: Tre Date: Mon, 18 Nov 2024 16:32:46 +0000 Subject: [PATCH 10/20] [Ownership] Assign test files to platform security team (#199795) ## Summary Assign test files to platform security team Contributes to: #192979 --------- Co-authored-by: Elastic Machine Co-authored-by: Jeramy Soucy --- .github/CODEOWNERS | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7b4cd85b0d8d0..e51fceb9e2e0f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1586,6 +1586,8 @@ x-pack/test/api_integration/deployment_agnostic/services/ @elastic/appex-qa x-pack/test/**/deployment_agnostic/ @elastic/appex-qa #temporarily to monitor tests migration # Core +/test/api_integration/apis/general/*.js @elastic/kibana-core # Assigned per https://github.com/elastic/kibana/pull/199795/files/894a8ede3f9d0398c5af56bf5a82654a9bc0610b#r1846691639 +/x-pack/test/plugin_api_integration/plugins/feature_usage_test @elastic/kibana-core /test/plugin_functional/plugins/rendering_plugin @elastic/kibana-core /test/plugin_functional/plugins/session_notifications @elastic/kibana-core /x-pack/test/cloud_integration/plugins/saml_provider @elastic/kibana-core @@ -1642,6 +1644,28 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib #CC# /x-pack/plugins/translations/ @elastic/kibana-localization @elastic/kibana-core # Kibana Platform Security +# security +/x-pack/test_serverless/functional/test_suites/observability/role_management @elastic/kibana-security +/x-pack/test/functional/config_security_basic.ts @elastic/kibana-security +/x-pack/test/functional/page_objects/user_profile_page.ts @elastic/kibana-security +/x-pack/test/functional/page_objects/space_selector_page.ts @elastic/kibana-security +/x-pack/test/functional/page_objects/security_page.ts @elastic/kibana-security +/x-pack/test/functional/page_objects/role_mappings_page.ts @elastic/kibana-security +/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts @elastic/kibana-security # Assigned per https://github.com/elastic/kibana/pull/39002 +/x-pack/test/functional/page_objects/api_keys_page.ts @elastic/kibana-security +/x-pack/test/functional/page_objects/account_settings_page.ts @elastic/kibana-security +/x-pack/test/functional/apps/user_profiles @elastic/kibana-security +/x-pack/test/common/services/spaces.ts @elastic/kibana-security +/x-pack/test/api_integration/config_security_*.ts @elastic/kibana-security +/x-pack/test/functional/apps/api_keys @elastic/kibana-security +/x-pack/test/ftr_apis/security_and_spaces @elastic/kibana-security +/test/server_integration/services/supertest.js @elastic/kibana-security @elastic/kibana-core +/test/server_integration/http/ssl @elastic/kibana-security # Assigned per https://github.com/elastic/kibana/pull/53810 +/test/server_integration/http/ssl_with_p12 @elastic/kibana-security # Assigned per https://github.com/elastic/kibana/pull/199795#discussion_r1846522206 +/test/server_integration/http/ssl_with_p12_intermediate @elastic/kibana-security # Assigned per https://github.com/elastic/kibana/pull/199795#discussion_r1846522206 + +/test/server_integration/config.base.js @elastic/kibana-security @elastic/kibana-core # Assigned per https://github.com/elastic/kibana/pull/199795#discussion_r1846510782 +/test/server_integration/__fixtures__ @elastic/kibana-security # Assigned per https://github.com/elastic/kibana/pull/53810 /.github/codeql @elastic/kibana-security /.github/workflows/codeql.yml @elastic/kibana-security /.github/workflows/codeql-stats.yml @elastic/kibana-security From 5de75c0fbe2f01491864be027d22dd776fdf00fb Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Mon, 18 Nov 2024 17:44:49 +0100 Subject: [PATCH 11/20] Update roles in serverless for value lists (#200128) Update serverless role for value lists related to this [PR](https://github.com/elastic/elasticsearch-controller/pull/771) --- .../kibana_roles/project_controller_security_roles.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml b/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml index 2d80c9d398210..22b3fd31c423b 100644 --- a/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml +++ b/x-pack/test_serverless/shared/lib/security/kibana_roles/project_controller_security_roles.yml @@ -151,6 +151,8 @@ t1_analyst: - write - maintenance - names: + - .lists* + - .items* - apm-*-transaction* - traces-apm* - auditbeat-* @@ -275,6 +277,7 @@ t3_analyst: privileges: - read - write + - view_index_metadata - names: - metrics-endpoint.metadata_current_* - .fleet-agents* @@ -406,6 +409,7 @@ rule_author: privileges: - read - write + - view_index_metadata - names: - metrics-endpoint.metadata_current_* - .fleet-agents* @@ -475,6 +479,7 @@ soc_manager: privileges: - read - write + - view_index_metadata - names: - metrics-endpoint.metadata_current_* - .fleet-agents* From 9e4e8a5cf66c56d60179ced36d4587b36d6dd605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 18 Nov 2024 17:45:27 +0100 Subject: [PATCH 12/20] [Obs AI Assistant] Support querying `semantic_text` fields in search connectors (#200184) This adds support for querying search connectors that have `semantic_text` fields. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../server/plugin.ts | 42 +-- .../server/service/index.ts | 17 +- .../service/knowledge_base_service/index.ts | 27 +- .../recall_from_connectors.ts | 139 --------- .../recall_from_search_connectors.ts | 272 ++++++++++++++++++ .../observability_ai_assistant/tsconfig.json | 1 + .../tests/knowledge_base/helpers.ts | 8 +- 7 files changed, 297 insertions(+), 209 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_connectors.ts create mode 100644 x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_search_connectors.ts diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts index b2b5736fd1d6f..361d13e6d77f2 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts @@ -12,14 +12,13 @@ import { Plugin, PluginInitializerContext, } from '@kbn/core/server'; -import { mapValues, once } from 'lodash'; +import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; import { CONNECTOR_TOKEN_SAVED_OBJECT_TYPE, ACTION_SAVED_OBJECT_TYPE, ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, } from '@kbn/actions-plugin/server/constants/saved_objects'; -import { firstValueFrom } from 'rxjs'; import { KibanaFeatureScope } from '@kbn/features-plugin/common'; import { OBSERVABILITY_AI_ASSISTANT_FEATURE_ID } from '../common/feature'; import type { ObservabilityAIAssistantConfig } from './config'; @@ -114,47 +113,10 @@ export class ObservabilityAIAssistantPlugin }; }) as ObservabilityAIAssistantRouteHandlerResources['plugins']; - // Using once to make sure the same model ID is used during service init and Knowledge base setup - const getSearchConnectorModelId = once(async () => { - const defaultModelId = '.elser_model_2'; - const [_, pluginsStart] = await core.getStartServices(); - // Wait for the license to be available so the ML plugin's guards pass once we ask for ELSER stats - const license = await firstValueFrom(pluginsStart.licensing.license$); - if (!license.hasAtLeast('enterprise')) { - return defaultModelId; - } - - try { - // Wait for the ML plugin's dependency on the internal saved objects client to be ready - const { ml } = await core.plugins.onSetup('ml'); - - if (!ml.found) { - throw new Error('Could not find ML plugin'); - } - - const elserModelDefinition = await ( - ml.contract as { - trainedModelsProvider: ( - request: {}, - soClient: {} - ) => { getELSER: () => Promise<{ model_id: string }> }; - } - ) - .trainedModelsProvider({} as any, {} as any) // request, savedObjectsClient (but we fake it to use the internal user) - .getELSER(); - - return elserModelDefinition.model_id; - } catch (error) { - this.logger.error(`Failed to resolve ELSER model definition: ${error}`); - return defaultModelId; - } - }); - const service = (this.service = new ObservabilityAIAssistantService({ logger: this.logger.get('service'), core, - getSearchConnectorModelId, - enableKnowledgeBase: this.config.enableKnowledgeBase, + config: this.config, })); registerMigrateKnowledgeBaseEntriesTask({ diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts index 6dcfbf1796501..9c26bebdd8388 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/index.ts @@ -20,6 +20,7 @@ import { conversationComponentTemplate } from './conversation_component_template import { kbComponentTemplate } from './kb_component_template'; import { KnowledgeBaseService } from './knowledge_base_service'; import type { RegistrationCallback, RespondFunctionResources } from './types'; +import { ObservabilityAIAssistantConfig } from '../config'; function getResourceName(resource: string) { return `.kibana-observability-ai-assistant-${resource}`; @@ -47,27 +48,23 @@ export const resourceNames = { export class ObservabilityAIAssistantService { private readonly core: CoreSetup; private readonly logger: Logger; - private readonly getSearchConnectorModelId: () => Promise; private kbService?: KnowledgeBaseService; - private enableKnowledgeBase: boolean; + private config: ObservabilityAIAssistantConfig; private readonly registrations: RegistrationCallback[] = []; constructor({ logger, core, - getSearchConnectorModelId, - enableKnowledgeBase, + config, }: { logger: Logger; core: CoreSetup; - getSearchConnectorModelId: () => Promise; - enableKnowledgeBase: boolean; + config: ObservabilityAIAssistantConfig; }) { this.core = core; this.logger = logger; - this.getSearchConnectorModelId = getSearchConnectorModelId; - this.enableKnowledgeBase = enableKnowledgeBase; + this.config = config; this.resetInit(); } @@ -166,12 +163,12 @@ export class ObservabilityAIAssistantService { }); this.kbService = new KnowledgeBaseService({ + core: this.core, logger: this.logger.get('kb'), + config: this.config, esClient: { asInternalUser, }, - getSearchConnectorModelId: this.getSearchConnectorModelId, - enabled: this.enableKnowledgeBase, }); this.logger.info('Successfully set up index assets'); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/index.ts index 66a49cdc29bee..a98cf6f810f2c 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/index.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/index.ts @@ -6,7 +6,7 @@ */ import { serverUnavailable } from '@hapi/boom'; -import type { ElasticsearchClient, IUiSettingsClient } from '@kbn/core/server'; +import type { CoreSetup, ElasticsearchClient, IUiSettingsClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; import { orderBy } from 'lodash'; import { encode } from 'gpt-tokenizer'; @@ -26,15 +26,17 @@ import { getInferenceEndpoint, isInferenceEndpointMissingOrUnavailable, } from '../inference_endpoint'; -import { recallFromConnectors } from './recall_from_connectors'; +import { recallFromSearchConnectors } from './recall_from_search_connectors'; +import { ObservabilityAIAssistantPluginStartDependencies } from '../../types'; +import { ObservabilityAIAssistantConfig } from '../../config'; interface Dependencies { + core: CoreSetup; esClient: { asInternalUser: ElasticsearchClient; }; logger: Logger; - getSearchConnectorModelId: () => Promise; - enabled: boolean; + config: ObservabilityAIAssistantConfig; } export interface RecalledEntry { @@ -141,14 +143,13 @@ export class KnowledgeBaseService { esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }; uiSettingsClient: IUiSettingsClient; }): Promise => { - if (!this.dependencies.enabled) { + if (!this.dependencies.config.enableKnowledgeBase) { return []; } this.dependencies.logger.debug( () => `Recalling entries from KB for queries: "${JSON.stringify(queries)}"` ); - const modelId = await this.dependencies.getSearchConnectorModelId(); const [documentsFromKb, documentsFromConnectors] = await Promise.all([ this.recallFromKnowledgeBase({ @@ -162,11 +163,11 @@ export class KnowledgeBaseService { } throw error; }), - recallFromConnectors({ + recallFromSearchConnectors({ esClient, uiSettingsClient, queries, - modelId, + core: this.dependencies.core, logger: this.dependencies.logger, }).catch((error) => { this.dependencies.logger.debug('Error getting data from search indices'); @@ -214,7 +215,7 @@ export class KnowledgeBaseService { namespace: string, user?: { name: string } ): Promise> => { - if (!this.dependencies.enabled) { + if (!this.dependencies.config.enableKnowledgeBase) { return []; } try { @@ -257,7 +258,7 @@ export class KnowledgeBaseService { sortBy?: string; sortDirection?: 'asc' | 'desc'; }): Promise<{ entries: KnowledgeBaseEntry[] }> => { - if (!this.dependencies.enabled) { + if (!this.dependencies.config.enableKnowledgeBase) { return { entries: [] }; } try { @@ -330,7 +331,7 @@ export class KnowledgeBaseService { user?: { name: string; id?: string }; namespace?: string; }) => { - if (!this.dependencies.enabled) { + if (!this.dependencies.config.enableKnowledgeBase) { return null; } const res = await this.dependencies.esClient.asInternalUser.search({ @@ -393,7 +394,7 @@ export class KnowledgeBaseService { user?: { name: string; id?: string }; namespace?: string; }): Promise => { - if (!this.dependencies.enabled) { + if (!this.dependencies.config.enableKnowledgeBase) { return; } @@ -448,7 +449,7 @@ export class KnowledgeBaseService { errorMessage = error.message; }); - const enabled = this.dependencies.enabled; + const enabled = this.dependencies.config.enableKnowledgeBase; if (!endpoint) { return { ready: false, enabled, errorMessage }; } diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_connectors.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_connectors.ts deleted file mode 100644 index 27c133e7b88d0..0000000000000 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_connectors.ts +++ /dev/null @@ -1,139 +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 { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { IUiSettingsClient } from '@kbn/core-ui-settings-server'; -import { isEmpty } from 'lodash'; -import type { Logger } from '@kbn/logging'; -import { RecalledEntry } from '.'; -import { aiAssistantSearchConnectorIndexPattern } from '../../../common'; - -export async function recallFromConnectors({ - queries, - esClient, - uiSettingsClient, - modelId, - logger, -}: { - queries: Array<{ text: string; boost?: number }>; - esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }; - uiSettingsClient: IUiSettingsClient; - modelId: string; - logger: Logger; -}): Promise { - const ML_INFERENCE_PREFIX = 'ml.inference.'; - const connectorIndices = await getConnectorIndices(esClient, uiSettingsClient, logger); - logger.debug(`Found connector indices: ${connectorIndices}`); - - const fieldCaps = await esClient.asCurrentUser.fieldCaps({ - index: connectorIndices, - fields: `${ML_INFERENCE_PREFIX}*`, - allow_no_indices: true, - types: ['sparse_vector'], - filters: '-metadata,-parent', - }); - - const fieldsWithVectors = Object.keys(fieldCaps.fields).map((field) => - field.replace('_expanded.predicted_value', '').replace(ML_INFERENCE_PREFIX, '') - ); - - if (!fieldsWithVectors.length) { - return []; - } - - const esQueries = fieldsWithVectors.flatMap((field) => { - const vectorField = `${ML_INFERENCE_PREFIX}${field}_expanded.predicted_value`; - const modelField = `${ML_INFERENCE_PREFIX}${field}_expanded.model_id`; - - return queries.map(({ text, boost = 1 }) => { - return { - bool: { - should: [ - { - text_expansion: { - [vectorField]: { - model_text: text, - model_id: modelId, - boost, - }, - }, - }, - ], - filter: [ - { - term: { - [modelField]: modelId, - }, - }, - ], - }, - }; - }); - }); - - const response = await esClient.asCurrentUser.search({ - index: connectorIndices, - query: { - bool: { - should: esQueries, - }, - }, - size: 20, - _source: { - exclude: ['_*', 'ml*'], - }, - }); - - const results = response.hits.hits.map((hit) => ({ - text: JSON.stringify(hit._source), - score: hit._score!, - is_correction: false, - id: hit._id!, - })); - - return results; -} - -async function getConnectorIndices( - esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }, - uiSettingsClient: IUiSettingsClient, - logger: Logger -) { - // improve performance by running this in parallel with the `uiSettingsClient` request - const responsePromise = esClient.asInternalUser.transport - .request<{ - results?: Array<{ index_name: string }>; - }>({ - method: 'GET', - path: '_connector', - querystring: { - filter_path: 'results.index_name', - }, - }) - .catch((e) => { - logger.warn(`Failed to fetch connector indices due to ${e.message}`); - return { results: [] }; - }); - - const customSearchConnectorIndex = await uiSettingsClient.get( - aiAssistantSearchConnectorIndexPattern - ); - - if (customSearchConnectorIndex) { - return customSearchConnectorIndex.split(','); - } - - const response = await responsePromise; - const connectorIndices = response.results?.map((result) => result.index_name); - - // preserve backwards compatibility with 8.14 (may not be needed in the future) - if (isEmpty(connectorIndices)) { - return ['search-*']; - } - - return connectorIndices; -} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_search_connectors.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_search_connectors.ts new file mode 100644 index 0000000000000..5abd6d850a8f4 --- /dev/null +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/knowledge_base_service/recall_from_search_connectors.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-server'; +import { isEmpty, orderBy, compact } from 'lodash'; +import type { Logger } from '@kbn/logging'; +import { CoreSetup } from '@kbn/core-lifecycle-server'; +import { firstValueFrom } from 'rxjs'; +import { RecalledEntry } from '.'; +import { aiAssistantSearchConnectorIndexPattern } from '../../../common'; +import { ObservabilityAIAssistantPluginStartDependencies } from '../../types'; + +export async function recallFromSearchConnectors({ + queries, + esClient, + uiSettingsClient, + logger, + core, +}: { + queries: Array<{ text: string; boost?: number }>; + esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }; + uiSettingsClient: IUiSettingsClient; + logger: Logger; + core: CoreSetup; +}): Promise { + const connectorIndices = await getConnectorIndices(esClient, uiSettingsClient, logger); + logger.debug(`Found connector indices: ${connectorIndices}`); + + const [semanticTextConnectors, legacyConnectors] = await Promise.all([ + recallFromSemanticTextConnectors({ + queries, + esClient, + uiSettingsClient, + logger, + core, + connectorIndices, + }), + + recallFromLegacyConnectors({ + queries, + esClient, + uiSettingsClient, + logger, + core, + connectorIndices, + }), + ]); + + return orderBy([...semanticTextConnectors, ...legacyConnectors], (entry) => entry.score, 'desc'); +} + +async function recallFromSemanticTextConnectors({ + queries, + esClient, + logger, + core, + connectorIndices, +}: { + queries: Array<{ text: string; boost?: number }>; + esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }; + uiSettingsClient: IUiSettingsClient; + logger: Logger; + core: CoreSetup; + connectorIndices: string[] | undefined; +}): Promise { + const fieldCaps = await esClient.asCurrentUser.fieldCaps({ + index: connectorIndices, + fields: `*`, + allow_no_indices: true, + types: ['semantic_text'], + filters: '-metadata,-parent', + }); + + const semanticTextFields = Object.keys(fieldCaps.fields); + if (!semanticTextFields.length) { + return []; + } + logger.debug(`Semantic text field for search connectors: ${semanticTextFields}`); + + const params = { + index: connectorIndices, + size: 20, + _source: { + excludes: semanticTextFields.map((field) => `${field}.inference`), + }, + query: { + bool: { + should: semanticTextFields.flatMap((field) => { + return queries.map(({ text, boost = 1 }) => ({ + bool: { filter: [{ semantic: { field, query: text, boost } }] }, + })); + }), + minimum_should_match: 1, + }, + }, + }; + + const response = await esClient.asCurrentUser.search(params); + + const results = response.hits.hits.map((hit) => ({ + text: JSON.stringify(hit._source), + score: hit._score!, + is_correction: false, + id: hit._id!, + })); + + return results; +} + +async function recallFromLegacyConnectors({ + queries, + esClient, + logger, + core, + connectorIndices, +}: { + queries: Array<{ text: string; boost?: number }>; + esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }; + uiSettingsClient: IUiSettingsClient; + logger: Logger; + core: CoreSetup; + connectorIndices: string[] | undefined; +}): Promise { + const ML_INFERENCE_PREFIX = 'ml.inference.'; + + const modelIdPromise = getElserModelId(core, logger); // pre-fetch modelId in parallel with fieldCaps + const fieldCaps = await esClient.asCurrentUser.fieldCaps({ + index: connectorIndices, + fields: `${ML_INFERENCE_PREFIX}*`, + allow_no_indices: true, + types: ['sparse_vector'], + filters: '-metadata,-parent', + }); + + const fieldsWithVectors = Object.keys(fieldCaps.fields).map((field) => + field.replace('_expanded.predicted_value', '').replace(ML_INFERENCE_PREFIX, '') + ); + + if (!fieldsWithVectors.length) { + return []; + } + + const modelId = await modelIdPromise; + const esQueries = fieldsWithVectors.flatMap((field) => { + const vectorField = `${ML_INFERENCE_PREFIX}${field}_expanded.predicted_value`; + const modelField = `${ML_INFERENCE_PREFIX}${field}_expanded.model_id`; + + return queries.map(({ text, boost = 1 }) => { + return { + bool: { + should: [ + { + text_expansion: { + [vectorField]: { + model_text: text, + model_id: modelId, + boost, + }, + }, + }, + ], + filter: [ + { + term: { + [modelField]: modelId, + }, + }, + ], + }, + }; + }); + }); + + const response = await esClient.asCurrentUser.search({ + index: connectorIndices, + size: 20, + _source: { + exclude: ['_*', 'ml*'], + }, + query: { + bool: { + should: esQueries, + }, + }, + }); + + const results = response.hits.hits.map((hit) => ({ + text: JSON.stringify(hit._source), + score: hit._score!, + is_correction: false, + id: hit._id!, + })); + + return results; +} + +async function getConnectorIndices( + esClient: { asCurrentUser: ElasticsearchClient; asInternalUser: ElasticsearchClient }, + uiSettingsClient: IUiSettingsClient, + logger: Logger +) { + // improve performance by running this in parallel with the `uiSettingsClient` request + const responsePromise = esClient.asInternalUser.connector + .list({ filter_path: 'results.index_name' }) + .catch((e) => { + logger.warn(`Failed to fetch connector indices due to ${e.message}`); + return { results: [] }; + }); + + const customSearchConnectorIndex = await uiSettingsClient.get( + aiAssistantSearchConnectorIndexPattern + ); + + if (customSearchConnectorIndex) { + return customSearchConnectorIndex.split(','); + } + + const response = await responsePromise; + + const connectorIndices = compact(response.results?.map((result) => result.index_name)); + + // preserve backwards compatibility with 8.14 (may not be needed in the future) + if (isEmpty(connectorIndices)) { + return ['search-*']; + } + + return connectorIndices; +} + +async function getElserModelId( + core: CoreSetup, + logger: Logger +) { + const defaultModelId = '.elser_model_2'; + const [_, pluginsStart] = await core.getStartServices(); + + // Wait for the license to be available so the ML plugin's guards pass once we ask for ELSER stats + const license = await firstValueFrom(pluginsStart.licensing.license$); + if (!license.hasAtLeast('enterprise')) { + return defaultModelId; + } + + try { + // Wait for the ML plugin's dependency on the internal saved objects client to be ready + const { ml } = await core.plugins.onSetup('ml'); + + if (!ml.found) { + throw new Error('Could not find ML plugin'); + } + + const elserModelDefinition = await ( + ml.contract as { + trainedModelsProvider: ( + request: {}, + soClient: {} + ) => { getELSER: () => Promise<{ model_id: string }> }; + } + ) + .trainedModelsProvider({} as any, {} as any) // request, savedObjectsClient (but we fake it to use the internal user) + .getELSER(); + + return elserModelDefinition.model_id; + } catch (error) { + logger.error(`Failed to resolve ELSER model definition: ${error}`); + return defaultModelId; + } +} diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json index 750bf69477653..d5acd7a365b50 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json @@ -46,6 +46,7 @@ "@kbn/management-settings-ids", "@kbn/ai-assistant-common", "@kbn/inference-common", + "@kbn/core-lifecycle-server", ], "exclude": ["target/**/*"] } diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/helpers.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/helpers.ts index fa1f15ddca4cd..25bbeb183a3b6 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/helpers.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/helpers.ts @@ -63,11 +63,5 @@ export async function deleteInferenceEndpoint({ es: Client; name?: string; }) { - return es.transport.request({ - method: 'DELETE', - path: `_inference/sparse_embedding/${name}`, - querystring: { - force: true, - }, - }); + return es.inference.delete({ inference_id: name, force: true }); } From eddfe5b8a73ac636d5e75606280bd771a01b1adb Mon Sep 17 00:00:00 2001 From: Kyra Cho Date: Mon, 18 Nov 2024 08:46:33 -0800 Subject: [PATCH 13/20] [Lens] rewrite BucketNestingEditor tests to use react testing library (#199888) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Hi! This PR rewrites the `BucketNestingEditor` tests to use the react testing library. Fixes #199839 Screenshot 2024-11-12 at 3 35 06 PM ### Checklist - [n/a] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --------- Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> --- .../bucket_nesting_editor.test.tsx | 107 ++++++++---------- 1 file changed, 49 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/bucket_nesting_editor.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/bucket_nesting_editor.test.tsx index 6c09849df04ac..0479162855659 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/bucket_nesting_editor.test.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/dimension_panel/bucket_nesting_editor.test.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { mount } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import React from 'react'; import { BucketNestingEditor } from './bucket_nesting_editor'; import { GenericIndexPatternColumn } from '../form_based'; @@ -21,7 +22,7 @@ const getFieldByName = (name: string): IndexPatternField | undefined => fieldMap describe('BucketNestingEditor', () => { function mockCol(col: Partial = {}): GenericIndexPatternColumn { - const result = { + return { dataType: 'string', isBucketed: true, label: 'a', @@ -33,13 +34,11 @@ describe('BucketNestingEditor', () => { }, sourceField: 'a', ...col, - }; - - return result as GenericIndexPatternColumn; + } as GenericIndexPatternColumn; } it('should display the top level grouping when at the root', () => { - const component = mount( + render( { setColumns={jest.fn()} /> ); - const nestingSwitch = component.find('[data-test-subj="indexPattern-nesting-switch"]').first(); - expect(nestingSwitch.prop('checked')).toBeTruthy(); + const nestingSwitch = screen.getByTestId('indexPattern-nesting-switch'); + expect(nestingSwitch).toBeChecked(); }); it('should display the bottom level grouping when appropriate', () => { - const component = mount( + render( { setColumns={jest.fn()} /> ); - const nestingSwitch = component.find('[data-test-subj="indexPattern-nesting-switch"]').first(); - expect(nestingSwitch.prop('checked')).toBeFalsy(); + const nestingSwitch = screen.getByTestId('indexPattern-nesting-switch'); + expect(nestingSwitch).not.toBeChecked(); }); - it('should reorder the columns when toggled', () => { + it('should reorder the columns when toggled', async () => { const setColumns = jest.fn(); - const component = mount( + const { rerender } = render( { /> ); - component - .find('[data-test-subj="indexPattern-nesting-switch"] button') - .first() - .simulate('click'); - + await userEvent.click(screen.getByTestId('indexPattern-nesting-switch')); expect(setColumns).toHaveBeenCalledTimes(1); expect(setColumns).toHaveBeenCalledWith(['a', 'b', 'c']); - component.setProps({ - layer: { - columnOrder: ['a', 'b', 'c'], - columns: { - a: mockCol(), - b: mockCol(), - c: mockCol({ operationType: 'min', isBucketed: false }), - }, - indexPatternId: 'foo', - }, - }); - - component - .find('[data-test-subj="indexPattern-nesting-switch"] button') - .first() - .simulate('click'); + rerender( + + ); + await userEvent.click(screen.getByTestId('indexPattern-nesting-switch')); expect(setColumns).toHaveBeenCalledTimes(2); expect(setColumns).toHaveBeenLastCalledWith(['b', 'a', 'c']); }); it('should display nothing if there are no buckets', () => { - const component = mount( + const { container } = render( { /> ); - expect(component.children().length).toBe(0); + expect(container.firstChild).toBeNull(); }); it('should display nothing if there is one bucket', () => { - const component = mount( + const { container } = render( { /> ); - expect(component.children().length).toBe(0); + expect(container.firstChild).toBeNull(); }); it('should display a dropdown with the parent column selected if 3+ buckets', () => { - const component = mount( + render( { /> ); - const control = component.find('[data-test-subj="indexPattern-nesting-select"]').first(); - - expect(control.prop('value')).toEqual('c'); + const control = screen.getByTestId('indexPattern-nesting-select'); + expect((control as HTMLSelectElement).value).toEqual('c'); }); - it('should reorder the columns when a column is selected in the dropdown', () => { + it('should reorder the columns when a column is selected in the dropdown', async () => { const setColumns = jest.fn(); - const component = mount( + render( { /> ); - const control = component.find('[data-test-subj="indexPattern-nesting-select"] select').first(); - control.simulate('change', { - target: { value: 'b' }, - }); + const control = screen.getByTestId('indexPattern-nesting-select'); + await userEvent.selectOptions(control, 'b'); expect(setColumns).toHaveBeenCalledWith(['c', 'b', 'a']); }); - it('should move to root if the first dropdown item is selected', () => { + it('should move to root if the first dropdown item is selected', async () => { const setColumns = jest.fn(); - const component = mount( + render( { /> ); - const control = component.find('[data-test-subj="indexPattern-nesting-select"] select').first(); - control.simulate('change', { target: { value: '' } }); + const control = screen.getByTestId('indexPattern-nesting-select'); + await userEvent.selectOptions(control, ''); expect(setColumns).toHaveBeenCalledWith(['a', 'c', 'b']); }); - it('should allow the last bucket to be moved', () => { + it('should allow the last bucket to be moved', async () => { const setColumns = jest.fn(); - const component = mount( + render( { /> ); - const control = component.find('[data-test-subj="indexPattern-nesting-select"] select').first(); - control.simulate('change', { - target: { value: '' }, - }); + const control = screen.getByTestId('indexPattern-nesting-select'); + await userEvent.selectOptions(control, ''); expect(setColumns).toHaveBeenCalledWith(['b', 'c', 'a']); }); From 81919c3d77f2d061540577cb8a889ca86bd0241d Mon Sep 17 00:00:00 2001 From: ruby Date: Tue, 19 Nov 2024 01:14:32 +0800 Subject: [PATCH 14/20] [Console]: incorrect position of console tour #198389 (#198636) --- src/plugins/console/common/constants/index.ts | 2 +- .../console/common/constants/plugin.ts | 2 ++ .../components/console_tour_step.tsx | 25 ++++++++++++++++-- .../embeddable/console_resize_button.tsx | 17 ++++++++++++ .../apps/console/_onboarding_tour.ts | 26 ++++++++++++++++--- test/functional/page_objects/console_page.ts | 6 ++++- 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/plugins/console/common/constants/index.ts b/src/plugins/console/common/constants/index.ts index a00bcebcf38cc..b4d6a594241ce 100644 --- a/src/plugins/console/common/constants/index.ts +++ b/src/plugins/console/common/constants/index.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export { MAJOR_VERSION } from './plugin'; +export { MAJOR_VERSION, WELCOME_TOUR_DELAY } from './plugin'; export { API_BASE_PATH, KIBANA_API_PREFIX } from './api'; export { DEFAULT_VARIABLES } from './variables'; export { diff --git a/src/plugins/console/common/constants/plugin.ts b/src/plugins/console/common/constants/plugin.ts index 27ddb7d5dff1d..bb87e300c138d 100644 --- a/src/plugins/console/common/constants/plugin.ts +++ b/src/plugins/console/common/constants/plugin.ts @@ -8,3 +8,5 @@ */ export const MAJOR_VERSION = '8.0.0'; + +export const WELCOME_TOUR_DELAY = 250; diff --git a/src/plugins/console/public/application/components/console_tour_step.tsx b/src/plugins/console/public/application/components/console_tour_step.tsx index 578d590bfff4a..97e999b0090aa 100644 --- a/src/plugins/console/public/application/components/console_tour_step.tsx +++ b/src/plugins/console/public/application/components/console_tour_step.tsx @@ -7,8 +7,9 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { ReactNode, ReactElement } from 'react'; +import React, { ReactNode, ReactElement, useState, useEffect } from 'react'; import { EuiTourStep, PopoverAnchorPosition } from '@elastic/eui'; +import { WELCOME_TOUR_DELAY } from '../../../common/constants'; export interface ConsoleTourStepProps { step: number; @@ -44,11 +45,31 @@ export const ConsoleTourStep = ({ tourStepProps, children }: Props) => { css, } = tourStepProps; + const [popoverVisible, setPopoverVisible] = useState(false); + + useEffect(() => { + let timeoutId: any; + + if (isStepOpen) { + timeoutId = setTimeout(() => { + setPopoverVisible(true); + }, WELCOME_TOUR_DELAY); + } else { + setPopoverVisible(false); + } + + return () => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }; + }, [isStepOpen]); + return ( { + const debouncedResize = debounce(() => { + window.dispatchEvent(new Event('resize')); + }, WELCOME_TOUR_DELAY); + + debouncedResize(); + + // Cleanup the debounce instance on unmount or dependency change + return () => { + debouncedResize.cancel(); + }; + }, [consoleHeight]); + useEffect(() => { function handleResize() { const newMaxConsoleHeight = getCurrentConsoleMaxSize(euiTheme); diff --git a/test/functional/apps/console/_onboarding_tour.ts b/test/functional/apps/console/_onboarding_tour.ts index 330498cb7b5ec..1fc47a70d14b0 100644 --- a/test/functional/apps/console/_onboarding_tour.ts +++ b/test/functional/apps/console/_onboarding_tour.ts @@ -10,6 +10,9 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +// The euiTour shows with a small delay, so with 1s we should be safe +const DELAY_FOR = 1000; + export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const browser = getService('browser'); @@ -40,22 +43,30 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await isTourStepOpen('filesTourStep')).to.be(false); }; + const waitUntilFinishedLoading = async () => { + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.common.sleep(DELAY_FOR); + }; + it('displays all five steps in the tour', async () => { + const andWaitFor = DELAY_FOR; + await waitUntilFinishedLoading(); + log.debug('on Shell tour step'); expect(await isTourStepOpen('shellTourStep')).to.be(true); - await PageObjects.console.clickNextTourStep(); + await PageObjects.console.clickNextTourStep(andWaitFor); log.debug('on Editor tour step'); expect(await isTourStepOpen('editorTourStep')).to.be(true); - await PageObjects.console.clickNextTourStep(); + await PageObjects.console.clickNextTourStep(andWaitFor); log.debug('on History tour step'); expect(await isTourStepOpen('historyTourStep')).to.be(true); - await PageObjects.console.clickNextTourStep(); + await PageObjects.console.clickNextTourStep(andWaitFor); log.debug('on Config tour step'); expect(await isTourStepOpen('configTourStep')).to.be(true); - await PageObjects.console.clickNextTourStep(); + await PageObjects.console.clickNextTourStep(andWaitFor); log.debug('on Files tour step'); expect(await isTourStepOpen('filesTourStep')).to.be(true); @@ -73,10 +84,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Tour should reset after clearing local storage await browser.clearLocalStorage(); await browser.refresh(); + + await waitUntilFinishedLoading(); expect(await isTourStepOpen('shellTourStep')).to.be(true); }); it('skipping the tour hides the tour steps', async () => { + await waitUntilFinishedLoading(); + expect(await isTourStepOpen('shellTourStep')).to.be(true); expect(await testSubjects.exists('consoleSkipTourButton')).to.be(true); await PageObjects.console.clickSkipTour(); @@ -90,6 +105,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('allows re-running the tour', async () => { + await waitUntilFinishedLoading(); + await PageObjects.console.skipTourIfExists(); // Verify that tour is hiddern @@ -100,6 +117,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.console.clickRerunTour(); // Verify that first tour step is visible + await waitUntilFinishedLoading(); expect(await isTourStepOpen('shellTourStep')).to.be(true); }); }); diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index 87308d24fd8c4..a80f3426e256e 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -276,8 +276,12 @@ export class ConsolePageObject extends FtrService { await this.testSubjects.click('consoleSkipTourButton'); } - public async clickNextTourStep() { + public async clickNextTourStep(andWaitFor: number = 0) { await this.testSubjects.click('consoleNextTourStepButton'); + + if (andWaitFor) { + await this.common.sleep(andWaitFor); + } } public async clickCompleteTour() { From 9d314b9fbb6dc850da7341949765243cd0bf742c Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Mon, 18 Nov 2024 18:28:49 +0100 Subject: [PATCH 15/20] [APM] Migrate time_range_metadata test to deployment agnostic (#200146) ## Summary Closes [#198994](https://github.com/elastic/kibana/issues/198994) Part of https://github.com/elastic/kibana/issues/193245 This PR contains the changes to migrate `time_range_metadata` test folder to Deployment-agnostic testing strategy. ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` It's recommended to be run against [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` - [ ] ~(OPTIONAL, only if a test has been unskipped) Run flaky test suite~ - [x] local run for serverless - [x] local run for stateful - [x] MKI run for serverless --------- Co-authored-by: Elastic Machine Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../apis/observability/apm/index.ts | 1 + .../apm/time_range_metadata/index.ts | 15 + .../many_apm_server_versions.spec.ts | 276 +++++++++++++++++ .../time_range_metadata.spec.ts | 68 ++--- .../many_apm_server_versions.spec.ts | 278 ------------------ 5 files changed, 324 insertions(+), 314 deletions(-) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/index.ts create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/many_apm_server_versions.spec.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/time_range_metadata/time_range_metadata.spec.ts (94%) delete mode 100644 x-pack/test/apm_api_integration/tests/time_range_metadata/many_apm_server_versions.spec.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index dcbf8edc4a755..640bb90abe711 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -32,6 +32,7 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./service_maps')); loadTestFile(require.resolve('./inspect')); loadTestFile(require.resolve('./service_groups')); + loadTestFile(require.resolve('./time_range_metadata')); loadTestFile(require.resolve('./diagnostics')); loadTestFile(require.resolve('./service_nodes')); loadTestFile(require.resolve('./span_links')); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/index.ts new file mode 100644 index 0000000000000..4e3c25936a2db --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('time_range_metadata', () => { + loadTestFile(require.resolve('./many_apm_server_versions.spec.ts')); + loadTestFile(require.resolve('./time_range_metadata.spec.ts')); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/many_apm_server_versions.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/many_apm_server_versions.spec.ts new file mode 100644 index 0000000000000..31012e6dd6d63 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/many_apm_server_versions.spec.ts @@ -0,0 +1,276 @@ +/* + * Copyright 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 { apm, timerange } from '@kbn/apm-synthtrace-client'; +import moment from 'moment'; +import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import { + TRANSACTION_DURATION_HISTOGRAM, + TRANSACTION_DURATION_SUMMARY, +} from '@kbn/apm-plugin/common/es_fields/apm'; +import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; +import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; +import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types'; +import { Readable } from 'stream'; +import type { ApmApiClient } from '../../../../services/apm_api'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + const es = getService('es'); + + const baseTime = new Date('2023-10-01T00:00:00.000Z').getTime(); + const startLegacy = moment(baseTime).add(0, 'minutes'); + const start = moment(baseTime).add(5, 'minutes'); + const endLegacy = moment(baseTime).add(10, 'minutes'); + const end = moment(baseTime).add(15, 'minutes'); + + describe('Time range metadata when there are multiple APM Server versions', () => { + describe('when ingesting traces from APM Server with different versions', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + + before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await generateTraceDataForService({ + serviceName: 'synth-java-legacy', + start: startLegacy, + end: endLegacy, + isLegacy: true, + synthtrace: apmSynthtraceEsClient, + }); + + await generateTraceDataForService({ + serviceName: 'synth-java', + start, + end, + isLegacy: false, + synthtrace: apmSynthtraceEsClient, + }); + }); + + after(() => { + return apmSynthtraceEsClient.clean(); + }); + + it('ingests transaction metrics with transaction.duration.summary', async () => { + const res = await es.search({ + index: 'metrics-apm*', + body: { + query: { + bool: { + filter: [ + { exists: { field: TRANSACTION_DURATION_HISTOGRAM } }, + { exists: { field: TRANSACTION_DURATION_SUMMARY } }, + ], + }, + }, + }, + }); + + // @ts-expect-error + expect(res.hits.total.value).to.be(20); + }); + + it('ingests transaction metrics without transaction.duration.summary', async () => { + const res = await es.search({ + index: 'metrics-apm*', + body: { + query: { + bool: { + filter: [{ exists: { field: TRANSACTION_DURATION_HISTOGRAM } }], + must_not: [{ exists: { field: TRANSACTION_DURATION_SUMMARY } }], + }, + }, + }, + }); + + // @ts-expect-error + expect(res.hits.total.value).to.be(10); + }); + + it('has transaction.duration.summary field for every document type', async () => { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/time_range_metadata', + params: { + query: { + start: endLegacy.toISOString(), + end: end.toISOString(), + enableContinuousRollups: true, + enableServiceTransactionMetrics: true, + useSpanName: false, + kuery: '', + }, + }, + }); + + const allHasSummaryField = response.body.sources + .filter( + (source) => + source.documentType !== ApmDocumentType.TransactionEvent && + source.rollupInterval !== RollupInterval.SixtyMinutes // there is not enough data for 60 minutes + ) + .every((source) => { + return source.hasDurationSummaryField; + }); + + expect(allHasSummaryField).to.eql(true); + }); + + it('does not support transaction.duration.summary when the field is not supported by all APM server versions', async () => { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/time_range_metadata', + params: { + query: { + start: startLegacy.toISOString(), + end: endLegacy.toISOString(), + enableContinuousRollups: true, + enableServiceTransactionMetrics: true, + useSpanName: false, + kuery: '', + }, + }, + }); + + const allHasSummaryField = response.body.sources.every((source) => { + return source.hasDurationSummaryField; + }); + + expect(allHasSummaryField).to.eql(false); + }); + + it('does not support transaction.duration.summary for transactionMetric 1m when not all documents within the range support it ', async () => { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/time_range_metadata', + params: { + query: { + start: startLegacy.toISOString(), + end: end.toISOString(), + enableContinuousRollups: true, + enableServiceTransactionMetrics: true, + useSpanName: false, + kuery: '', + }, + }, + }); + + const hasDurationSummaryField = response.body.sources.find( + (source) => + source.documentType === ApmDocumentType.TransactionMetric && + source.rollupInterval === RollupInterval.OneMinute // there is not enough data for 60 minutes in the timerange defined for the tests + )?.hasDurationSummaryField; + + expect(hasDurationSummaryField).to.eql(false); + }); + + it('does not have latency data for synth-java-legacy', async () => { + const res = await getLatencyChartForService({ + serviceName: 'synth-java-legacy', + start, + end: endLegacy, + apmApiClient, + useDurationSummary: true, + }); + + expect(res.body.currentPeriod.latencyTimeseries.map(({ y }) => y)).to.eql([ + null, + null, + null, + null, + null, + null, + ]); + }); + + it('has latency data for synth-java service', async () => { + const res = await getLatencyChartForService({ + serviceName: 'synth-java', + start, + end: endLegacy, + apmApiClient, + useDurationSummary: true, + }); + + expect(res.body.currentPeriod.latencyTimeseries.map(({ y }) => y)).to.eql([ + 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, + ]); + }); + }); + }); +} + +// This will retrieve latency data expecting the `transaction.duration.summary` field to be present +function getLatencyChartForService({ + serviceName, + start, + end, + apmApiClient, + useDurationSummary, +}: { + serviceName: string; + start: moment.Moment; + end: moment.Moment; + apmApiClient: ApmApiClient; + useDurationSummary: boolean; +}) { + return apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/{serviceName}/transactions/charts/latency`, + params: { + path: { serviceName }, + query: { + start: start.toISOString(), + end: end.toISOString(), + environment: 'production', + latencyAggregationType: LatencyAggregationType.avg, + transactionType: 'request', + kuery: '', + documentType: ApmDocumentType.TransactionMetric, + rollupInterval: RollupInterval.OneMinute, + bucketSizeInSeconds: 60, + useDurationSummary, + }, + }, + }); +} + +function generateTraceDataForService({ + serviceName, + start, + end, + isLegacy, + synthtrace, +}: { + serviceName: string; + start: moment.Moment; + end: moment.Moment; + isLegacy?: boolean; + synthtrace: ApmSynthtraceEsClient; +}) { + const instance = apm + .service({ + name: serviceName, + environment: 'production', + agentName: 'java', + }) + .instance(`instance`); + + const events = timerange(start, end) + .ratePerMinute(6) + .generator((timestamp) => + instance + .transaction({ transactionName: 'GET /order/{id}' }) + .timestamp(timestamp) + .duration(1000) + .success() + ); + + const apmPipeline = (base: Readable) => { + return synthtrace.getDefaultPipeline({ versionOverride: '8.5.0' })(base); + }; + + return synthtrace.index(events, isLegacy ? apmPipeline : undefined); +} diff --git a/x-pack/test/apm_api_integration/tests/time_range_metadata/time_range_metadata.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/time_range_metadata.spec.ts similarity index 94% rename from x-pack/test/apm_api_integration/tests/time_range_metadata/time_range_metadata.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/time_range_metadata.spec.ts index 6ea90a1b8b1d2..7ec73a692f988 100644 --- a/x-pack/test/apm_api_integration/tests/time_range_metadata/time_range_metadata.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/time_range_metadata/time_range_metadata.spec.ts @@ -11,15 +11,14 @@ import { omit, sortBy } from 'lodash'; import moment, { Moment } from 'moment'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { Readable } from 'stream'; import { ToolingLog } from '@kbn/tooling-log'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const es = getService('es'); const log = getService('log'); @@ -55,29 +54,28 @@ export default function ApiTest({ getService }: FtrProviderContext) { }; } - registry.when('Time range metadata without data', { config: 'basic', archives: [] }, () => { - it('handles empty state', async () => { - const response = await getTimeRangeMedata({ - start, - end, - }); + describe('Time range metadata', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + describe('without data', () => { + it('handles empty state', async () => { + const response = await getTimeRangeMedata({ + start, + end, + }); - expect(response.isUsingServiceDestinationMetrics).to.eql(false); - expect(response.sources.filter((source) => source.hasDocs)).to.eql([ - { - documentType: ApmDocumentType.TransactionEvent, - rollupInterval: RollupInterval.None, - hasDocs: true, - hasDurationSummaryField: false, - }, - ]); + expect(response.isUsingServiceDestinationMetrics).to.eql(false); + expect(response.sources.filter((source) => source.hasDocs)).to.eql([ + { + documentType: ApmDocumentType.TransactionEvent, + rollupInterval: RollupInterval.None, + hasDocs: true, + hasDurationSummaryField: false, + }, + ]); + }); }); - }); - registry.when( - 'Time range metadata when generating data with multiple APM server versions', - { config: 'basic', archives: [] }, - () => { + describe('when generating data with multiple APM server versions', () => { describe('data loaded with and without summary field', () => { const withoutSummaryFieldStart = moment('2023-04-28T00:00:00.000Z'); const withoutSummaryFieldEnd = moment(withoutSummaryFieldStart).add(2, 'hours'); @@ -86,6 +84,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const withSummaryFieldEnd = moment(withSummaryFieldStart).add(2, 'hours'); before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); await getTransactionEvents({ start: withoutSummaryFieldStart, end: withoutSummaryFieldEnd, @@ -259,15 +258,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); }); - } - ); - - registry.when( - 'Time range metadata when generating data', - { config: 'basic', archives: [] }, - () => { - before(() => { + }); + + describe('when generating data', () => { + before(async () => { const instance = apm.service('my-service', 'production', 'java').instance('instance'); + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); return apmSynthtraceEsClient.index( timerange(moment(start).subtract(1, 'day'), end) @@ -620,8 +616,8 @@ export default function ApiTest({ getService }: FtrProviderContext) { ]); }); }); - } - ); + }); + }); } function getTransactionEvents({ diff --git a/x-pack/test/apm_api_integration/tests/time_range_metadata/many_apm_server_versions.spec.ts b/x-pack/test/apm_api_integration/tests/time_range_metadata/many_apm_server_versions.spec.ts deleted file mode 100644 index 6031b7dd8de5b..0000000000000 --- a/x-pack/test/apm_api_integration/tests/time_range_metadata/many_apm_server_versions.spec.ts +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import expect from '@kbn/expect'; -import { apm, timerange } from '@kbn/apm-synthtrace-client'; -import moment from 'moment'; -import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; -import { - TRANSACTION_DURATION_HISTOGRAM, - TRANSACTION_DURATION_SUMMARY, -} from '@kbn/apm-plugin/common/es_fields/apm'; -import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; -import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; -import { LatencyAggregationType } from '@kbn/apm-plugin/common/latency_aggregation_types'; -import { Readable } from 'stream'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { ApmApiClient } from '../../common/config'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const synthtrace = getService('apmSynthtraceEsClient'); - const es = getService('es'); - - const baseTime = new Date('2023-10-01T00:00:00.000Z').getTime(); - const startLegacy = moment(baseTime).add(0, 'minutes'); - const start = moment(baseTime).add(5, 'minutes'); - const endLegacy = moment(baseTime).add(10, 'minutes'); - const end = moment(baseTime).add(15, 'minutes'); - - registry.when( - 'Time range metadata when there are multiple APM Server versions', - { config: 'basic', archives: [] }, - () => { - describe('when ingesting traces from APM Server with different versions', () => { - before(async () => { - await generateTraceDataForService({ - serviceName: 'synth-java-legacy', - start: startLegacy, - end: endLegacy, - isLegacy: true, - synthtrace, - }); - - await generateTraceDataForService({ - serviceName: 'synth-java', - start, - end, - isLegacy: false, - synthtrace, - }); - }); - - after(() => { - return synthtrace.clean(); - }); - - it('ingests transaction metrics with transaction.duration.summary', async () => { - const res = await es.search({ - index: 'metrics-apm*', - body: { - query: { - bool: { - filter: [ - { exists: { field: TRANSACTION_DURATION_HISTOGRAM } }, - { exists: { field: TRANSACTION_DURATION_SUMMARY } }, - ], - }, - }, - }, - }); - - // @ts-expect-error - expect(res.hits.total.value).to.be(20); - }); - - it('ingests transaction metrics without transaction.duration.summary', async () => { - const res = await es.search({ - index: 'metrics-apm*', - body: { - query: { - bool: { - filter: [{ exists: { field: TRANSACTION_DURATION_HISTOGRAM } }], - must_not: [{ exists: { field: TRANSACTION_DURATION_SUMMARY } }], - }, - }, - }, - }); - - // @ts-expect-error - expect(res.hits.total.value).to.be(10); - }); - - it('has transaction.duration.summary field for every document type', async () => { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/time_range_metadata', - params: { - query: { - start: endLegacy.toISOString(), - end: end.toISOString(), - enableContinuousRollups: true, - enableServiceTransactionMetrics: true, - useSpanName: false, - kuery: '', - }, - }, - }); - - const allHasSummaryField = response.body.sources - .filter( - (source) => - source.documentType !== ApmDocumentType.TransactionEvent && - source.rollupInterval !== RollupInterval.SixtyMinutes // there is not enough data for 60 minutes - ) - .every((source) => { - return source.hasDurationSummaryField; - }); - - expect(allHasSummaryField).to.eql(true); - }); - - it('does not support transaction.duration.summary when the field is not supported by all APM server versions', async () => { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/time_range_metadata', - params: { - query: { - start: startLegacy.toISOString(), - end: endLegacy.toISOString(), - enableContinuousRollups: true, - enableServiceTransactionMetrics: true, - useSpanName: false, - kuery: '', - }, - }, - }); - - const allHasSummaryField = response.body.sources.every((source) => { - return source.hasDurationSummaryField; - }); - - expect(allHasSummaryField).to.eql(false); - }); - - it('does not support transaction.duration.summary for transactionMetric 1m when not all documents within the range support it ', async () => { - const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/time_range_metadata', - params: { - query: { - start: startLegacy.toISOString(), - end: end.toISOString(), - enableContinuousRollups: true, - enableServiceTransactionMetrics: true, - useSpanName: false, - kuery: '', - }, - }, - }); - - const hasDurationSummaryField = response.body.sources.find( - (source) => - source.documentType === ApmDocumentType.TransactionMetric && - source.rollupInterval === RollupInterval.OneMinute // there is not enough data for 60 minutes in the timerange defined for the tests - )?.hasDurationSummaryField; - - expect(hasDurationSummaryField).to.eql(false); - }); - - it('does not have latency data for synth-java-legacy', async () => { - const res = await getLatencyChartForService({ - serviceName: 'synth-java-legacy', - start, - end: endLegacy, - apmApiClient, - useDurationSummary: true, - }); - - expect(res.body.currentPeriod.latencyTimeseries.map(({ y }) => y)).to.eql([ - null, - null, - null, - null, - null, - null, - ]); - }); - - it('has latency data for synth-java service', async () => { - const res = await getLatencyChartForService({ - serviceName: 'synth-java', - start, - end: endLegacy, - apmApiClient, - useDurationSummary: true, - }); - - expect(res.body.currentPeriod.latencyTimeseries.map(({ y }) => y)).to.eql([ - 1000000, 1000000, 1000000, 1000000, 1000000, 1000000, - ]); - }); - }); - } - ); -} - -// This will retrieve latency data expecting the `transaction.duration.summary` field to be present -function getLatencyChartForService({ - serviceName, - start, - end, - apmApiClient, - useDurationSummary, -}: { - serviceName: string; - start: moment.Moment; - end: moment.Moment; - apmApiClient: ApmApiClient; - useDurationSummary: boolean; -}) { - return apmApiClient.readUser({ - endpoint: `GET /internal/apm/services/{serviceName}/transactions/charts/latency`, - params: { - path: { serviceName }, - query: { - start: start.toISOString(), - end: end.toISOString(), - environment: 'production', - latencyAggregationType: LatencyAggregationType.avg, - transactionType: 'request', - kuery: '', - documentType: ApmDocumentType.TransactionMetric, - rollupInterval: RollupInterval.OneMinute, - bucketSizeInSeconds: 60, - useDurationSummary, - }, - }, - }); -} - -function generateTraceDataForService({ - serviceName, - start, - end, - isLegacy, - synthtrace, -}: { - serviceName: string; - start: moment.Moment; - end: moment.Moment; - isLegacy?: boolean; - synthtrace: ApmSynthtraceEsClient; -}) { - const instance = apm - .service({ - name: serviceName, - environment: 'production', - agentName: 'java', - }) - .instance(`instance`); - - const events = timerange(start, end) - .ratePerMinute(6) - .generator((timestamp) => - instance - .transaction({ transactionName: 'GET /order/{id}' }) - .timestamp(timestamp) - .duration(1000) - .success() - ); - - const apmPipeline = (base: Readable) => { - return synthtrace.getDefaultPipeline({ versionOverride: '8.5.0' })(base); - }; - - return synthtrace.index(events, isLegacy ? apmPipeline : undefined); -} From 8664415750699bad6624b19ba3acab55fa54b9f9 Mon Sep 17 00:00:00 2001 From: Milosz Marcinkowski <38698566+miloszmarcinkowski@users.noreply.github.com> Date: Mon, 18 Nov 2024 18:45:52 +0100 Subject: [PATCH 16/20] Migrate `/test/apm_api_integration/tests/suggestions` to be deployment agnostic api tests (#200556) closes #198992 closes #198993 part of https://github.com/elastic/kibana/issues/193245 ### How to test - Serverless ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/serverless/oblt.serverless.config.ts --grep="APM" ``` - Stateful ``` node scripts/functional_tests_server --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts node scripts/functional_test_runner --config x-pack/test/api_integration/deployment_agnostic/configs/stateful/oblt.stateful.config.ts --grep="APM" ``` - [MKI](https://github.com/crespocarlos/kibana/blob/main/x-pack/test_serverless/README.md#run-tests-on-mki) ### Checklist - [x] (OPTIONAL, only if a test has been unskipped) Run flaky test suite - [x] serverless - [x] stateful - [x] MKI --------- Co-authored-by: Sergi Romeu --- .../apis/observability/apm/index.ts | 2 ++ .../apm}/suggestions/generate_data.ts | 0 .../observability/apm/suggestions/index.ts | 14 ++++++++++++++ .../apm}/suggestions/suggestions.spec.ts | 17 ++++++++++------- .../apm}/throughput/dependencies_apis.spec.ts | 18 ++++++++++-------- .../observability/apm/throughput/index.ts | 16 ++++++++++++++++ .../apm}/throughput/service_apis.spec.ts | 18 ++++++++++-------- .../apm}/throughput/service_maps.spec.ts | 19 ++++++++++--------- 8 files changed, 72 insertions(+), 32 deletions(-) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/suggestions/generate_data.ts (100%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/index.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/suggestions/suggestions.spec.ts (94%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/throughput/dependencies_apis.spec.ts (94%) create mode 100644 x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/index.ts rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/throughput/service_apis.spec.ts (92%) rename x-pack/test/{apm_api_integration/tests => api_integration/deployment_agnostic/apis/observability/apm}/throughput/service_maps.spec.ts (90%) diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts index 640bb90abe711..ab7f9e5736392 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/index.ts @@ -36,5 +36,7 @@ export default function apmApiIntegrationTests({ loadTestFile(require.resolve('./diagnostics')); loadTestFile(require.resolve('./service_nodes')); loadTestFile(require.resolve('./span_links')); + loadTestFile(require.resolve('./suggestions')); + loadTestFile(require.resolve('./throughput')); }); } diff --git a/x-pack/test/apm_api_integration/tests/suggestions/generate_data.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/generate_data.ts similarity index 100% rename from x-pack/test/apm_api_integration/tests/suggestions/generate_data.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/generate_data.ts diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/index.ts new file mode 100644 index 0000000000000..9b2563c093a9d --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Suggestions', () => { + loadTestFile(require.resolve('./suggestions.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/suggestions.spec.ts similarity index 94% rename from x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/suggestions.spec.ts index d4d1c3b141700..a6e9342885571 100644 --- a/x-pack/test/apm_api_integration/tests/suggestions/suggestions.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/suggestions/suggestions.spec.ts @@ -11,7 +11,8 @@ import { TRANSACTION_TYPE, } from '@kbn/apm-plugin/common/es_fields/apm'; import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; import { generateData } from './generate_data'; const startNumber = new Date('2021-01-01T00:00:00.000Z').getTime(); @@ -20,14 +21,16 @@ const endNumber = new Date('2021-01-01T00:05:00.000Z').getTime() - 1; const start = new Date(startNumber).toISOString(); const end = new Date(endNumber).toISOString(); -export default function suggestionsTests({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function suggestionsTests({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); + + describe('suggestions when data is loaded', () => { + let apmSynthtraceEsClient: ApmSynthtraceEsClient; - // FLAKY: https://github.com/elastic/kibana/issues/177538 - registry.when('suggestions when data is loaded', { config: 'basic', archives: [] }, async () => { before(async () => { + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); + await generateData({ apmSynthtraceEsClient, start: startNumber, diff --git a/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/dependencies_apis.spec.ts similarity index 94% rename from x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/dependencies_apis.spec.ts index fe591631fafe7..84d293f287b2f 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/dependencies_apis.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/dependencies_apis.spec.ts @@ -8,13 +8,13 @@ import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { meanBy, sumBy } from 'lodash'; import { DependencyNode, ServiceNode } from '@kbn/apm-plugin/common/connections'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { roundNumber } from '../../utils'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { roundNumber } from '../utils/common'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; @@ -93,11 +93,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { let throughputValues: Awaited>; - // FLAKY: https://github.com/elastic/kibana/issues/177536 - registry.when.skip('Dependencies throughput value', { config: 'basic', archives: [] }, () => { + describe('Dependencies throughput value', () => { describe('when data is loaded', () => { const GO_PROD_RATE = 75; const JAVA_PROD_RATE = 25; + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { const serviceGoProdInstance = apm .service({ name: 'synth-go', environment: 'production', agentName: 'go' }) @@ -105,6 +106,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const serviceJavaInstance = apm .service({ name: 'synth-java', environment: 'development', agentName: 'java' }) .instance('instance-c'); + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); await apmSynthtraceEsClient.index([ timerange(start, end) diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/index.ts new file mode 100644 index 0000000000000..e0176b18be783 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) { + describe('Throughput', () => { + loadTestFile(require.resolve('./dependencies_apis.spec.ts')); + loadTestFile(require.resolve('./service_apis.spec.ts')); + loadTestFile(require.resolve('./service_maps.spec.ts')); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/service_apis.spec.ts similarity index 92% rename from x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/service_apis.spec.ts index 9d69ce74bf0ea..429d29090a1d2 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_apis.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/service_apis.spec.ts @@ -11,13 +11,13 @@ import { apm, timerange } from '@kbn/apm-synthtrace-client'; import expect from '@kbn/expect'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { meanBy, sumBy } from 'lodash'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { roundNumber } from '../../utils'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { roundNumber } from '../utils/common'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const serviceName = 'synth-go'; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); @@ -141,11 +141,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { let throughputMetricValues: Awaited>; let throughputTransactionValues: Awaited>; - // FLAKY: https://github.com/elastic/kibana/issues/177535 - registry.when('Services APIs', { config: 'basic', archives: [] }, () => { + describe('Services APIs', () => { describe('when data is loaded ', () => { const GO_PROD_RATE = 80; const GO_DEV_RATE = 20; + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { const serviceGoProdInstance = apm .service({ name: serviceName, environment: 'production', agentName: 'go' }) @@ -153,6 +154,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const serviceGoDevInstance = apm .service({ name: serviceName, environment: 'development', agentName: 'go' }) .instance('instance-b'); + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); await apmSynthtraceEsClient.index([ timerange(start, end) diff --git a/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/service_maps.spec.ts similarity index 90% rename from x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts rename to x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/service_maps.spec.ts index 5ee475344e286..883e81ea24524 100644 --- a/x-pack/test/apm_api_integration/tests/throughput/service_maps.spec.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/apm/throughput/service_maps.spec.ts @@ -9,13 +9,13 @@ import expect from '@kbn/expect'; import { ApmDocumentType } from '@kbn/apm-plugin/common/document_type'; import { RollupInterval } from '@kbn/apm-plugin/common/rollup'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { roundNumber } from '../../utils'; +import type { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; +import type { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { roundNumber } from '../utils/common'; -export default function ApiTest({ getService }: FtrProviderContext) { - const registry = getService('registry'); - const apmApiClient = getService('apmApiClient'); - const apmSynthtraceEsClient = getService('apmSynthtraceEsClient'); +export default function ApiTest({ getService }: DeploymentAgnosticFtrProviderContext) { + const apmApiClient = getService('apmApi'); + const synthtrace = getService('synthtrace'); const serviceName = 'synth-go'; const start = new Date('2021-01-01T00:00:00.000Z').getTime(); @@ -83,10 +83,12 @@ export default function ApiTest({ getService }: FtrProviderContext) { let throughputMetricValues: Awaited>; let throughputTransactionValues: Awaited>; - registry.when('Service Maps APIs', { config: 'trial', archives: [] }, () => { + describe('Service Maps APIs', () => { describe('when data is loaded ', () => { const GO_PROD_RATE = 80; const GO_DEV_RATE = 20; + let apmSynthtraceEsClient: ApmSynthtraceEsClient; + before(async () => { const serviceGoProdInstance = apm .service({ name: serviceName, environment: 'production', agentName: 'go' }) @@ -94,6 +96,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const serviceGoDevInstance = apm .service({ name: serviceName, environment: 'development', agentName: 'go' }) .instance('instance-b'); + apmSynthtraceEsClient = await synthtrace.createApmSynthtraceEsClient(); await apmSynthtraceEsClient.index([ timerange(start, end) @@ -119,7 +122,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { after(() => apmSynthtraceEsClient.clean()); - // FLAKY: https://github.com/elastic/kibana/issues/176984 describe('compare throughput value between service inventory and service maps', () => { before(async () => { [throughputTransactionValues, throughputMetricValues] = await Promise.all([ @@ -136,7 +138,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/176987 describe('when calling service maps transactions stats api', () => { let serviceMapsNodeThroughput: number | null | undefined; before(async () => { From 598076e215bda81ba4f35166be3cf1b8edc9026a Mon Sep 17 00:00:00 2001 From: Tre Date: Mon, 18 Nov 2024 17:50:21 +0000 Subject: [PATCH 17/20] [Ownership] Assign test files to presentation team (#200209) ## Summary Assign test files to presentation team Contributes to: #192979 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .github/CODEOWNERS | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e51fceb9e2e0f..760a4e0ba446e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1368,7 +1368,23 @@ x-pack/test_serverless/**/test_suites/observability/ai_assistant @elastic/obs-ai ### END Observability Plugins # Presentation -/x-pack/test/disable_ems @elastic/kibana-presentation +/test/interpreter_functional/snapshots @elastic/kibana-presentation # Assigned per https://github.com/elastic/kibana/pull/54342 +/test/functional/services/inspector.ts @elastic/kibana-presentation +/x-pack/test/functional/services/canvas_element.ts @elastic/kibana-presentation +/x-pack/test/functional/page_objects/canvas_page.ts @elastic/kibana-presentation +/x-pack/test/accessibility/apps/group3/canvas.ts @elastic/kibana-presentation +/x-pack/test/upgrade/apps/canvas @elastic/kibana-presentation +/x-pack/test/upgrade/apps/dashboard @elastic/kibana-presentation +/test/functional/screenshots/baseline/tsvb_dashboard.png @elastic/kibana-presentation +/test/functional/screenshots/baseline/dashboard_*.png @elastic/kibana-presentation +/test/functional/screenshots/baseline/area_chart.png @elastic/kibana-presentation +/x-pack/test/disable_ems @elastic/kibana-presentation # Assigned per https://github.com/elastic/kibana/pull/165986 +/x-pack/test/functional/fixtures/kbn_archiver/dashboard* @elastic/kibana-presentation +/test/functional/page_objects/dashboard_page* @elastic/kibana-presentation +/test/functional/firefox/dashboard.config.ts @elastic/kibana-presentation # Assigned per: https://github.com/elastic/kibana/issues/15023 +/test/functional/fixtures/es_archiver/dashboard @elastic/kibana-presentation # Assigned per: https://github.com/elastic/kibana/issues/15023 +/test/accessibility/apps/dashboard.ts @elastic/kibana-presentation +/test/accessibility/apps/filter_panel.ts @elastic/kibana-presentation /x-pack/test/functional/apps/dashboard @elastic/kibana-presentation /x-pack/test/accessibility/apps/group3/maps.ts @elastic/kibana-presentation /x-pack/test/accessibility/apps/group1/dashboard_panel_options.ts @elastic/kibana-presentation From cc38d8d03b664ae43d05e26c3c08ba68998e4348 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 18 Nov 2024 11:17:00 -0700 Subject: [PATCH 18/20] [Security solution] Assistant package + plugin dead code removal (#200235) --- .../schemas/knowledge_base/entries/mocks.ts | 19 - .../impl/utils/bedrock.ts | 4 +- .../impl/assistant/index.test.tsx | 11 +- .../quick_prompt_editor.tsx | 2 +- .../assistant/settings/assistant_settings.tsx | 1 - .../settings/evaluation_settings/utils.tsx | 25 - .../impl/assistant/settings/translations.ts | 7 - .../anonymization_settings/index.test.tsx | 1 - .../settings/anonymization_settings/index.tsx | 3 - .../index.tsx | 4 - .../context_editor/index.tsx | 2 - .../translations.ts | 50 - .../impl/knowledge_base/translations.ts | 28 - .../language_models/simple_chat_model.ts | 2 +- .../kbn-langchain/server/utils/bedrock.ts | 18 +- .../kbn-langchain/server/utils/types.ts | 22 - .../elastic_assistant/common/constants.ts | 5 - .../server/__mocks__/query_text.ts | 28 - .../server/__mocks__/response.ts | 10 - .../conversations/create_conversation.test.ts | 51 - ...en_and_acknowledged_alerts_qery_results.ts | 25 - .../server/lib/langchain/executors/types.ts | 13 - .../server/lib/langchain/graphs/index.ts | 2 - .../bulk_actions_route.ts | 2 - .../attack_discovery/helpers/helpers.ts | 17 - .../server/routes/helpers.ts | 177 --- .../server/routes/knowledge_base/constants.ts | 4 - .../entries/bulk_actions_route.ts | 2 - .../get_knowledge_base_indices.ts | 2 +- .../routes/prompts/bulk_actions_route.ts | 2 - .../user_conversations/bulk_actions_route.ts | 2 - .../elastic_assistant/server/routes/utils.ts | 4 - .../plugins/elastic_assistant/server/types.ts | 19 - .../custom_codeblock_markdown_plugin.tsx | 4 +- .../public/assistant/helpers.tsx | 18 - .../attack/mini_attack_chain/index.tsx | 2 +- .../use_add_to_existing_case/translations.ts | 9 - .../mock/mock_use_attack_discovery.ts | 84 - .../settings_modal/alerts_settings/index.tsx | 1 - .../public/attack_discovery/pages/helpers.ts | 7 - .../knowledge_base_retrieval_tool.ts | 2 +- .../knowledge_base_write_tool.ts | 2 +- .../mock_attack_discovery_chain_result.ts | 64 - ...en_and_acknowledged_alerts_qery_results.ts | 25 - ...n_and_acknowledged_alerts_query_results.ts | 1396 ----------------- .../tools/security_labs/security_labs_tool.ts | 2 +- .../translations/translations/fr-FR.json | 22 +- .../translations/translations/ja-JP.json | 22 +- .../translations/translations/zh-CN.json | 22 +- 49 files changed, 37 insertions(+), 2209 deletions(-) delete mode 100644 x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/entries/mocks.ts delete mode 100644 x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/evaluation_settings/utils.tsx delete mode 100644 x-pack/plugins/elastic_assistant/server/__mocks__/query_text.ts delete mode 100644 x-pack/plugins/elastic_assistant/server/lib/attack_discovery/graphs/default_attack_discovery_graph/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts delete mode 100644 x-pack/plugins/security_solution/server/assistant/tools/mock/mock_attack_discovery_chain_result.ts delete mode 100644 x-pack/plugins/security_solution/server/assistant/tools/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts delete mode 100644 x-pack/plugins/security_solution/server/assistant/tools/mock/mock_open_and_acknowledged_alerts_query_results.ts diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/entries/mocks.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/entries/mocks.ts deleted file mode 100644 index 24a43bd3182df..0000000000000 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/schemas/knowledge_base/entries/mocks.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IndexEntryCreateFields } from './common_attributes.gen'; - -export const indexEntryMock: IndexEntryCreateFields = { - type: 'index', - name: 'SpongBotSlackConnector', - namespace: 'default', - index: 'spongbot', - field: 'semantic_text', - description: "Use this index to search for the user's Slack messages.", - queryDescription: - 'The free text search that the user wants to perform over this dataset. So if asking "what are my slack messages from last week about failed tests", the query would be "A test has failed! failing test failed test".', -}; diff --git a/x-pack/packages/kbn-elastic-assistant-common/impl/utils/bedrock.ts b/x-pack/packages/kbn-elastic-assistant-common/impl/utils/bedrock.ts index ab3756d43dc0e..6d503d675796b 100644 --- a/x-pack/packages/kbn-elastic-assistant-common/impl/utils/bedrock.ts +++ b/x-pack/packages/kbn-elastic-assistant-common/impl/utils/bedrock.ts @@ -15,14 +15,14 @@ import { fromUtf8, toUtf8 } from '@smithy/util-utf8'; * @param {Uint8Array[]} chunks - Array of Uint8Array chunks to be parsed. * @returns {string} - Parsed string from the Bedrock buffer. */ -export const parseBedrockBuffer = (chunks: Uint8Array[], logger: Logger): string => { +export const parseBedrockBuffer = (chunks: Uint8Array[]): string => { // Initialize an empty Uint8Array to store the concatenated buffer. let bedrockBuffer: Uint8Array = new Uint8Array(0); // Map through each chunk to process the Bedrock buffer. return chunks .map((chunk) => { - const processedChunk = handleBedrockChunk({ chunk, bedrockBuffer, logger }); + const processedChunk = handleBedrockChunk({ chunk, bedrockBuffer }); bedrockBuffer = processedChunk.bedrockBuffer; return processedChunk.decodedChunk; }) diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx index 368477455c941..2fc6a603d8a82 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/index.test.tsx @@ -18,7 +18,7 @@ import { DefinedUseQueryResult, UseQueryResult } from '@tanstack/react-query'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import useSessionStorage from 'react-use/lib/useSessionStorage'; import { QuickPrompts } from './quick_prompts/quick_prompts'; -import { mockAssistantAvailability, TestProviders } from '../mock/test_providers/test_providers'; +import { TestProviders } from '../mock/test_providers/test_providers'; import { useFetchCurrentUserConversations } from './api'; import { Conversation } from '../assistant_context/types'; import * as all from './chat_send/use_chat_send'; @@ -54,7 +54,7 @@ const mockData = { }, }; -const renderAssistant = async (extraProps = {}, providerProps = {}) => { +const renderAssistant = async (extraProps = {}) => { const chatSendSpy = jest.spyOn(all, 'useChatSend'); const assistant = render( @@ -310,12 +310,7 @@ describe('Assistant', () => { describe('when not authorized', () => { it('should be disabled', async () => { - const { queryByTestId } = await renderAssistant( - {}, - { - assistantAvailability: { ...mockAssistantAvailability, isAssistantEnabled: false }, - } - ); + const { queryByTestId } = await renderAssistant({}); expect(queryByTestId('prompt-textarea')).toHaveProperty('disabled'); }); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_settings/quick_prompt_editor.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_settings/quick_prompt_editor.tsx index d4d9a9bd82c9f..f9705cedf2afb 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_settings/quick_prompt_editor.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/quick_prompts/quick_prompt_settings/quick_prompt_editor.tsx @@ -112,7 +112,7 @@ const QuickPromptSettingsEditorComponent = ({ ); const handleColorChange = useCallback( - (color, { hex, isValid }) => { + (color) => { if (selectedQuickPrompt != null) { setUpdatedQuickPromptSettings((prev) => { const alreadyExists = prev.some((qp) => qp.name === selectedQuickPrompt.name); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx index f325e411bae2b..cb78e98f205f2 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/assistant_settings.tsx @@ -257,7 +257,6 @@ export const AssistantSettings: React.FC = React.memo( )} {selectedSettingsTab === ANONYMIZATION_TAB && ( { - return `${basePath}/app/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:now-1y%2Fd,to:now))&_a=(columns:!(evaluationId,runName,totalAgents,totalInput,totalRequests,input,reference,prediction,evaluation.value,evaluation.reasoning,connectorName,connectorName.keyword,evaluation.__run.runId,evaluation.__run.runId.keyword,evaluation.score,evaluationEnd,evaluationId.keyword,evaluationStart,input.keyword,inputExampleId,inputExampleId.keyword,evaluationDuration,prediction.keyword,predictionResponse.reason.sendToLLM,predictionResponse.status,ConnectorId,predictionResponse.value.data,predictionResponse.value.data.keyword,predictionResponse.value.status,predictionResponse.value.trace_data.trace_id,predictionResponse.value.trace_data.trace_id.keyword,predictionResponse.value.trace_data.transaction_id,predictionResponse.value.trace_data.transaction_id.keyword,reference.keyword,runName.keyword),filters:!(),grid:(columns:('@timestamp':(width:212),ConnectorId:(width:133),connectorName:(width:181),connectorName.keyword:(width:229),evaluation.__run.runId:(width:282),evaluation.__run.runId.keyword:(width:245),evaluation.reasoning:(width:336),evaluation.reasoning.keyword:(width:232),evaluation.score:(width:209),evaluation.value:(width:156),evaluationDuration:(width:174),evaluationEnd:(width:151),evaluationId:(width:130),evaluationId.keyword:(width:186),evaluationStart:(width:202),input:(width:347),input.keyword:(width:458),prediction:(width:264),prediction.keyword:(width:313),predictionResponse.value.connector_id:(width:294),predictionResponse.value.trace_data.trace_id:(width:278),predictionResponse.value.trace_data.transaction_id.keyword:(width:177),reference:(width:305),reference.keyword:(width:219),runName:(width:405),totalAgents:(width:125),totalInput:(width:111),totalRequests:(width:138))),hideChart:!t,index:ce1b41cb-6298-4612-a33c-ba85b3c18ec7,interval:auto,query:(esql:'from%20.kibana-elastic-ai-assistant-evaluation-results%20%0A%7C%20keep%20@timestamp,%20evaluationId,%20runName,%20totalAgents,%20totalInput,%20totalRequests,%20input,%20reference,%20prediction,%20evaluation.value,%20evaluation.reasoning,%20connectorName,%20*%0A%7C%20drop%20evaluation.reasoning.keyword%0A%7C%20rename%20predictionResponse.value.connector_id%20as%20ConnectorId%0A%7C%20where%20evaluationId%20%3D%3D%20%22${evaluationId}%22%0A%7C%20sort%20@timestamp%20desc%0A%7C%20limit%20100%0A%0A%0A'),rowHeight:15,sort:!(!('@timestamp',desc)))`; -}; - -/** - * Link to APM Trace Explorer for viewing an evaluation - * @param basePath - * @param evaluationId - */ -export const getApmLink = (basePath: string, evaluationId: string) => { - return `${basePath}/app/apm/traces/explorer/waterfall?comparisonEnabled=false&detailTab=timeline&environment=ENVIRONMENT_ALL&kuery=&query=%22labels.evaluationId%22:%20%22${evaluationId}%22&rangeFrom=now-1y&rangeTo=now&showCriticalPath=false&traceId=451662121b1f5e6c44084ad7415b9409&transactionId=5f1392fa04766025&type=kql&waterfallItemId=`; -}; diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/translations.ts index be83f3a74e2af..67573033ba568 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/settings/translations.ts @@ -14,13 +14,6 @@ export const SETTINGS = i18n.translate( } ); -export const SETTINGS_TOOLTIP = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.settingsTooltip', - { - defaultMessage: 'Settings', - } -); - export const SECURITY_AI_SETTINGS = i18n.translate( 'xpack.elasticAssistant.assistant.settings.securityAiSettingsTitle', { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.test.tsx index 375d03581cb39..e94546ef4ce28 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.test.tsx @@ -13,7 +13,6 @@ import { AnonymizationSettings } from '.'; import type { Props } from '.'; const props: Props = { - defaultPageSize: 5, anonymizationFields: { total: 4, page: 1, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.tsx index 77d9a3602d849..29aa8265ccd0e 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings/index.tsx @@ -16,7 +16,6 @@ import * as i18n from './translations'; import { useAnonymizationListUpdate } from './use_anonymization_list_update'; export interface Props { - defaultPageSize?: number; anonymizationFields: FindAnonymizationFieldsResponse; anonymizationFieldsBulkActions: PerformAnonymizationFieldsBulkActionRequestBody; setAnonymizationFieldsBulkActions: React.Dispatch< @@ -28,7 +27,6 @@ export interface Props { } const AnonymizationSettingsComponent: React.FC = ({ - defaultPageSize, anonymizationFields, anonymizationFieldsBulkActions, setAnonymizationFieldsBulkActions, @@ -60,7 +58,6 @@ const AnonymizationSettingsComponent: React.FC = ({ anonymizationFields={anonymizationFields} onListUpdated={onListUpdated} rawData={null} - pageSize={defaultPageSize} compressed={true} /> diff --git a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings_management/index.tsx b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings_management/index.tsx index bb6ed94f546f0..3b8758afdd215 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings_management/index.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/data_anonymization/settings/anonymization_settings_management/index.tsx @@ -44,13 +44,11 @@ import { } from '../../../assistant/settings/translations'; export interface Props { - defaultPageSize?: number; modalMode?: boolean; onClose?: () => void; } const AnonymizationSettingsManagementComponent: React.FC = ({ - defaultPageSize = 5, modalMode = false, onClose, }) => { @@ -151,7 +149,6 @@ const AnonymizationSettingsManagementComponent: React.FC = ({ compressed={false} onListUpdated={onListUpdated} rawData={null} - pageSize={defaultPageSize} /> @@ -187,7 +184,6 @@ const AnonymizationSettingsManagementComponent: React.FC = ({ compressed={false} onListUpdated={onListUpdated} rawData={null} - pageSize={defaultPageSize} /> void; rawData: Record | null; - pageSize?: number; } const search: EuiSearchBarProps = { @@ -71,7 +70,6 @@ const ContextEditorComponent: React.FC = ({ compressed = true, onListUpdated, rawData, - pageSize = DEFAULT_PAGE_SIZE, }) => { const isAllSelected = useRef(false); // Must be a ref and not state in order not to re-render `selectionValue`, which fires `onSelectionChange` twice const { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts index 24784586edcdf..5101e0fa3ad4b 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/knowledge_base_settings_management/translations.ts @@ -56,27 +56,12 @@ export const COLUMN_ENTRIES = i18n.translate( } ); -export const COLUMN_SPACE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSpaceLabel', - { - defaultMessage: 'Space', - } -); - export const COLUMN_CREATED = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnCreatedLabel', { defaultMessage: 'Created', } ); - -export const COLUMN_ACTIONS = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnActionsLabel', - { - defaultMessage: 'Actions', - } -); - export const SEARCH_PLACEHOLDER = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.searchPlaceholder', { @@ -84,13 +69,6 @@ export const SEARCH_PLACEHOLDER = i18n.translate( } ); -export const DEFAULT_FLYOUT_TITLE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.defaultFlyoutTitle', - { - defaultMessage: 'Knowledge Base', - } -); - export const NEW_INDEX_FLYOUT_TITLE = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newIndexEntryFlyoutTitle', { @@ -126,27 +104,6 @@ export const MANUAL = i18n.translate( } ); -export const CREATE_INDEX_TITLE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.createIndexTitle', - { - defaultMessage: 'New Index entry', - } -); - -export const NEW_ENTRY_TITLE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newEntryTitle', - { - defaultMessage: 'New entry', - } -); - -export const DELETE_ENTRY_DEFAULT_TITLE = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryDefaultTitle', - { - defaultMessage: 'Delete item', - } -); - export const ENTRY_NAME_INPUT_LABEL = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputLabel', { @@ -309,13 +266,6 @@ export const ENTRY_OUTPUT_FIELDS_HELP_LABEL = i18n.translate( } ); -export const ENTRY_INPUT_PLACEHOLDER = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryInputPlaceholder', - { - defaultMessage: 'Input', - } -); - export const ENTRY_FIELD_PLACEHOLDER = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldPlaceholder', { diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts index 3666f94af3edb..eb6bf560a63dd 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts @@ -14,13 +14,6 @@ export const ALERTS_LABEL = i18n.translate( } ); -export const SEND_ALERTS_LABEL = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.sendAlertsLabel', - { - defaultMessage: 'Send Alerts', - } -); - export const LATEST_AND_RISKIEST_OPEN_ALERTS = (alertsCount: number) => i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel', @@ -115,24 +108,3 @@ export const KNOWLEDGE_BASE_ELSER_LABEL = i18n.translate( defaultMessage: 'ELSER Configured', } ); - -export const ESQL_LABEL = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlLabel', - { - defaultMessage: 'ES|QL Knowledge Base Documents', - } -); - -export const ESQL_DESCRIPTION = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlDescription', - { - defaultMessage: 'Knowledge Base docs for generating ES|QL queries', - } -); - -export const ESQL_DESCRIPTION_INSTALLED = i18n.translate( - 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlInstalledDescription', - { - defaultMessage: 'ES|QL Knowledge Base docs loaded', - } -); diff --git a/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts b/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts index a66d088345b22..787aed559e285 100644 --- a/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts +++ b/x-pack/packages/kbn-langchain/server/language_models/simple_chat_model.ts @@ -43,7 +43,7 @@ function _formatMessages(messages: BaseMessage[]) { if (!messages.length) { throw new Error('No messages provided.'); } - return messages.map((message, i) => { + return messages.map((message) => { if (typeof message.content !== 'string') { throw new Error('Multimodal messages are not supported.'); } diff --git a/x-pack/packages/kbn-langchain/server/utils/bedrock.ts b/x-pack/packages/kbn-langchain/server/utils/bedrock.ts index 1cb218f37d2fd..39e5e77864fef 100644 --- a/x-pack/packages/kbn-langchain/server/utils/bedrock.ts +++ b/x-pack/packages/kbn-langchain/server/utils/bedrock.ts @@ -24,7 +24,7 @@ export const parseBedrockStreamAsAsyncIterator = async function* ( } try { for await (const chunk of responseStream) { - const bedrockChunk = handleBedrockChunk({ chunk, bedrockBuffer: new Uint8Array(0), logger }); + const bedrockChunk = handleBedrockChunk({ chunk, bedrockBuffer: new Uint8Array(0) }); yield bedrockChunk.decodedChunk; } } catch (err) { @@ -46,7 +46,7 @@ export const parseBedrockStream: StreamParser = async ( if (abortSignal) { abortSignal.addEventListener('abort', () => { responseStream.destroy(new Error('Aborted')); - return parseBedrockBuffer(responseBuffer, logger); + return parseBedrockBuffer(responseBuffer); }); } responseStream.on('data', (chunk) => { @@ -55,7 +55,7 @@ export const parseBedrockStream: StreamParser = async ( if (tokenHandler) { // Initialize an empty Uint8Array to store the concatenated buffer. const bedrockBuffer: Uint8Array = new Uint8Array(0); - handleBedrockChunk({ chunk, bedrockBuffer, logger, chunkHandler: tokenHandler }); + handleBedrockChunk({ chunk, bedrockBuffer, chunkHandler: tokenHandler }); } }); @@ -67,7 +67,7 @@ export const parseBedrockStream: StreamParser = async ( } }); - return parseBedrockBuffer(responseBuffer, logger); + return parseBedrockBuffer(responseBuffer); }; /** @@ -76,14 +76,14 @@ export const parseBedrockStream: StreamParser = async ( * @param {Uint8Array[]} chunks - Array of Uint8Array chunks to be parsed. * @returns {string} - Parsed string from the Bedrock buffer. */ -const parseBedrockBuffer = (chunks: Uint8Array[], logger: Logger): string => { +const parseBedrockBuffer = (chunks: Uint8Array[]): string => { // Initialize an empty Uint8Array to store the concatenated buffer. let bedrockBuffer: Uint8Array = new Uint8Array(0); // Map through each chunk to process the Bedrock buffer. return chunks .map((chunk) => { - const processedChunk = handleBedrockChunk({ chunk, bedrockBuffer, logger }); + const processedChunk = handleBedrockChunk({ chunk, bedrockBuffer }); bedrockBuffer = processedChunk.bedrockBuffer; return processedChunk.decodedChunk; }) @@ -101,12 +101,10 @@ export const handleBedrockChunk = ({ chunk, bedrockBuffer, chunkHandler, - logger, }: { chunk: Uint8Array; bedrockBuffer: Uint8Array; chunkHandler?: (chunk: string) => void; - logger?: Logger; }): { decodedChunk: string; bedrockBuffer: Uint8Array } => { // Concatenate the current chunk to the existing buffer. let newBuffer = concatChunks(bedrockBuffer, chunk); @@ -135,7 +133,7 @@ export const handleBedrockChunk = ({ const body = JSON.parse( Buffer.from(JSON.parse(new TextDecoder().decode(event.body)).bytes, 'base64').toString() ); - const decodedContent = prepareBedrockOutput(body, logger); + const decodedContent = prepareBedrockOutput(body); if (chunkHandler) { chunkHandler(decodedContent); } @@ -193,7 +191,7 @@ interface CompletionChunk { * @param responseBody * @returns string */ -const prepareBedrockOutput = (responseBody: CompletionChunk, logger?: Logger): string => { +const prepareBedrockOutput = (responseBody: CompletionChunk): string => { if (responseBody.type && responseBody.type.length) { if (responseBody.type === 'message_start' && responseBody.message) { return parseContent(responseBody.message.content); diff --git a/x-pack/packages/kbn-langchain/server/utils/types.ts b/x-pack/packages/kbn-langchain/server/utils/types.ts index d88adb4045e87..273ed66e25797 100644 --- a/x-pack/packages/kbn-langchain/server/utils/types.ts +++ b/x-pack/packages/kbn-langchain/server/utils/types.ts @@ -14,25 +14,3 @@ export type StreamParser = ( abortSignal?: AbortSignal, tokenHandler?: (token: string) => void ) => Promise; - -export interface GeminiResponseSchema { - candidates: Candidate[]; - usageMetadata: { - promptTokenCount: number; - candidatesTokenCount: number; - totalTokenCount: number; - }; -} -interface Part { - text: string; -} - -interface Candidate { - content: Content; - finishReason: string; -} - -interface Content { - role: string; - parts: Part[]; -} diff --git a/x-pack/plugins/elastic_assistant/common/constants.ts b/x-pack/plugins/elastic_assistant/common/constants.ts index dd6e47e070591..3c3b016870d46 100755 --- a/x-pack/plugins/elastic_assistant/common/constants.ts +++ b/x-pack/plugins/elastic_assistant/common/constants.ts @@ -17,13 +17,8 @@ export const ATTACK_DISCOVERY = `${BASE_PATH}/attack_discovery`; export const ATTACK_DISCOVERY_BY_CONNECTOR_ID = `${ATTACK_DISCOVERY}/{connectorId}`; export const ATTACK_DISCOVERY_CANCEL_BY_CONNECTOR_ID = `${ATTACK_DISCOVERY}/cancel/{connectorId}`; -export const MAX_CONVERSATIONS_TO_UPDATE_IN_PARALLEL = 50; export const CONVERSATIONS_TABLE_MAX_PAGE_SIZE = 100; - -export const MAX_ANONYMIZATION_FIELDS_TO_UPDATE_IN_PARALLEL = 50; export const ANONYMIZATION_FIELDS_TABLE_MAX_PAGE_SIZE = 100; - -export const MAX_PROMPTS_TO_UPDATE_IN_PARALLEL = 50; export const PROMPTS_TABLE_MAX_PAGE_SIZE = 100; // Knowledge Base diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/query_text.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/query_text.ts deleted file mode 100644 index 1ea69b786ad1f..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/query_text.ts +++ /dev/null @@ -1,28 +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. - */ - -/** - * This mock query text is an example of a prompt that might be passed to - * the `ElasticSearchStore`'s `similaritySearch` function, as the `query` - * parameter. - * - * In the real world, an LLM extracted the `mockQueryText` from the - * following prompt, which includes a system prompt: - * - * ``` - * You are a helpful, expert assistant who answers questions about Elastic Security. Do not answer questions unrelated to Elastic Security. - * If you answer a question related to KQL, EQL, or ES|QL, it should be immediately usable within an Elastic Security timeline; please always format the output correctly with back ticks. Any answer provided for Query DSL should also be usable in a security timeline. This means you should only ever include the "filter" portion of the query. - * - * Use the following context to answer questions: - * - * Generate an ES|QL query that will count the number of connections made to external IP addresses, broken down by user. If the count is greater than 100 for a specific user, add a new field called "follow_up" that contains a value of "true", otherwise, it should contain "false". The user names should also be enriched with their respective group names. - * ``` - * - * In the example above, the LLM omitted the system prompt, such that only `mockQueryText` is passed to the `similaritySearch` function. - */ -export const mockQueryText = - 'Generate an ES|QL query that will count the number of connections made to external IP addresses, broken down by user. If the count is greater than 100 for a specific user, add a new field called follow_up that contains a value of true, otherwise, it should contain false. The user names should also be enriched with their respective group names.'; diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts index ae736c77c30ef..dc5a2ba0e884a 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/response.ts @@ -15,8 +15,6 @@ import { EsPromptsSchema } from '../ai_assistant_data_clients/prompts/types'; import { getPromptsSearchEsMock } from './prompts_schema.mock'; import { EsAnonymizationFieldsSchema } from '../ai_assistant_data_clients/anonymization_fields/types'; import { getAnonymizationFieldsSearchEsMock } from './anonymization_fields_schema.mock'; -import { getAttackDiscoverySearchEsMock } from './attack_discovery_schema.mock'; -import { EsAttackDiscoverySchema } from '../lib/attack_discovery/persistence/types'; export const responseMock = { create: httpServerMock.createResponseFactory, @@ -36,14 +34,6 @@ export const getFindConversationsResultWithSingleHit = (): FindResponse => ({ - page: 1, - perPage: 1, - total: 1, - data: getAttackDiscoverySearchEsMock(), - }); - export const getFindPromptsResultWithSingleHit = (): FindResponse => ({ page: 1, perPage: 1, diff --git a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts index 6fba2f9c8b606..7ef1f7865da36 100644 --- a/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts +++ b/x-pack/plugins/elastic_assistant/server/ai_assistant_data_clients/conversations/create_conversation.test.ts @@ -8,8 +8,6 @@ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { createConversation } from './create_conversation'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; -import { estypes } from '@elastic/elasticsearch'; -import { EsConversationSchema } from './types'; import { getConversation } from './get_conversation'; import { ConversationCreateProps, ConversationResponse } from '@kbn/elastic-assistant-common'; import { AuthenticatedUser } from '@kbn/core-security-common'; @@ -68,55 +66,6 @@ export const getConversationResponseMock = (): ConversationResponse => ({ ], }); -export const getSearchConversationMock = (): estypes.SearchResponse => ({ - _scroll_id: '123', - _shards: { - failed: 0, - skipped: 0, - successful: 0, - total: 0, - }, - hits: { - hits: [ - { - _id: '1', - _index: '', - _score: 0, - _source: { - '@timestamp': '2020-04-20T15:25:31.830Z', - created_at: '2020-04-20T15:25:31.830Z', - title: 'title-1', - updated_at: '2020-04-20T15:25:31.830Z', - messages: [], - category: 'assistant', - id: '1', - namespace: 'default', - is_default: true, - exclude_from_last_conversation_storage: false, - api_config: { - action_type_id: '.gen-ai', - connector_id: 'c1', - default_system_prompt_id: 'prompt-1', - model: 'test', - provider: 'Azure OpenAI', - }, - users: [ - { - id: '1111', - name: 'elastic', - }, - ], - replacements: undefined, - }, - }, - ], - max_score: 0, - total: 1, - }, - timed_out: false, - took: 10, -}); - describe('createConversation', () => { let logger: ReturnType; beforeEach(() => { diff --git a/x-pack/plugins/elastic_assistant/server/lib/attack_discovery/graphs/default_attack_discovery_graph/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts b/x-pack/plugins/elastic_assistant/server/lib/attack_discovery/graphs/default_attack_discovery_graph/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts deleted file mode 100644 index ed5549acc586a..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/attack_discovery/graphs/default_attack_discovery_graph/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const mockEmptyOpenAndAcknowledgedAlertsQueryResults = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 0, - relation: 'eq', - }, - max_score: null, - hits: [], - }, -}; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts index da560dfae72dd..7dea19755a686 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts @@ -75,19 +75,6 @@ export type AgentExecutor = ( params: AgentExecutorParams ) => Promise>; -export type AgentExecutorEvaluator = ( - langChainMessages: BaseMessage[], - exampleId?: string -) => Promise; - -export interface AgentExecutorEvaluatorWithMetadata { - agentEvaluator: AgentExecutorEvaluator; - metadata: { - connectorName: string; - runName: string; - }; -} - export interface TraceOptions { evaluationId?: string; exampleId?: string; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/index.ts index b9e4f85a800a0..c1027b835765d 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/index.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/graphs/index.ts @@ -21,8 +21,6 @@ export type GetAttackDiscoveryGraph = ( params: GetDefaultAttackDiscoveryGraphParams ) => DefaultAttackDiscoveryGraph; -export type GraphType = 'assistant' | 'attack-discovery'; - export interface AssistantGraphMetadata { getDefaultAssistantGraph: GetAssistantGraph; graphType: 'assistant'; diff --git a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts index 9aedffae5cfb5..170d0599de171 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/anonymization_fields/bulk_actions_route.ts @@ -48,8 +48,6 @@ export interface BulkOperationError { }; } -export type BulkActionError = BulkOperationError | unknown; - const buildBulkResponse = ( response: KibanaResponseFactory, { diff --git a/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers/helpers.ts b/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers/helpers.ts index 188976f0b3f5c..65d3cee1662c5 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/attack_discovery/helpers/helpers.ts @@ -15,9 +15,7 @@ import { GenerationInterval, Replacements, } from '@kbn/elastic-assistant-common'; -import { AnonymizationFieldResponse } from '@kbn/elastic-assistant-common/impl/schemas/anonymization_fields/bulk_crud_anonymization_fields_route.gen'; import type { Document } from '@langchain/core/documents'; -import { v4 as uuidv4 } from 'uuid'; import { Moment } from 'moment'; import { transformError } from '@kbn/securitysolution-es-utils'; import moment from 'moment/moment'; @@ -29,21 +27,6 @@ import { } from '../../../lib/telemetry/event_based_telemetry'; import { AttackDiscoveryDataClient } from '../../../lib/attack_discovery/persistence'; -export const REQUIRED_FOR_ATTACK_DISCOVERY: AnonymizationFieldResponse[] = [ - { - id: uuidv4(), - field: '_id', - allowed: true, - anonymized: true, - }, - { - id: uuidv4(), - field: 'kibana.alert.original_time', - allowed: true, - anonymized: false, - }, -]; - export const attackDiscoveryStatus: { [k: string]: AttackDiscoveryStatus } = { canceled: 'canceled', failed: 'failed', diff --git a/x-pack/plugins/elastic_assistant/server/routes/helpers.ts b/x-pack/plugins/elastic_assistant/server/routes/helpers.ts index e68efd8e71f8f..fcd051f1f2157 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/helpers.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/helpers.ts @@ -17,8 +17,6 @@ import { StreamResponseWithHeaders } from '@kbn/ml-response-stream/server'; import { TraceData, - ConversationResponse, - ExecuteConnectorRequestBody, Message, Replacements, replaceAnonymizedValuesWithOriginalValues, @@ -43,7 +41,6 @@ import { AssistantDataClients, StaticReturnType, } from '../lib/langchain/executors/types'; -import { executeAction, StaticResponse } from '../lib/executor'; import { getLangChainMessages } from '../lib/langchain/helpers'; import { AIAssistantConversationsDataClient } from '../ai_assistant_data_clients/conversations'; @@ -131,94 +128,6 @@ export const hasAIAssistantLicense = (license: ILicense): boolean => export const UPGRADE_LICENSE_MESSAGE = 'Your license does not support AI Assistant. Please upgrade your license.'; -export interface GenerateTitleForNewChatConversationParams { - message: Pick; - model?: string; - actionTypeId: string; - connectorId: string; - logger: Logger; - actionsClient: PublicMethodsOf; - responseLanguage?: string; -} -export const generateTitleForNewChatConversation = async ({ - message, - model, - actionTypeId, - connectorId, - logger, - actionsClient, - responseLanguage = 'English', -}: GenerateTitleForNewChatConversationParams) => { - try { - const autoTitle = (await executeAction({ - actionsClient, - connectorId, - actionTypeId, - params: { - subAction: 'invokeAI', - subActionParams: { - model, - messages: [ - { - role: 'system', - content: `You are a helpful assistant for Elastic Security. Assume the following message is the start of a conversation between you and a user; give this conversation a title based on the content below. DO NOT UNDER ANY CIRCUMSTANCES wrap this title in single or double quotes. This title is shown in a list of conversations to the user, so title it for the user, not for you. Please create the title in ${responseLanguage}.`, - }, - { - role: message.role, - content: message.content, - }, - ], - ...(actionTypeId === '.gen-ai' - ? { n: 1, stop: null, temperature: 0.2 } - : { temperature: 0, stopSequences: [] }), - }, - }, - logger, - })) as unknown as StaticResponse; // TODO: Use function overloads in executeAction to avoid this cast when sending subAction: 'invokeAI', - if (autoTitle.status === 'ok') { - // This regular expression captures a string enclosed in single or double quotes. - // It extracts the string content without the quotes. - // Example matches: - // - "Hello, World!" => Captures: Hello, World! - // - 'Another Example' => Captures: Another Example - // - JustTextWithoutQuotes => Captures: JustTextWithoutQuotes - const match = autoTitle.data.match(/^["']?([^"']+)["']?$/); - const title = match ? match[1] : autoTitle.data; - return title; - } - } catch (e) { - /* empty */ - } -}; - -export interface AppendMessageToConversationParams { - conversationsDataClient: AIAssistantConversationsDataClient; - messages: Array>; - replacements: Replacements; - conversation: ConversationResponse; -} -export const appendMessageToConversation = async ({ - conversationsDataClient, - messages, - replacements, - conversation, -}: AppendMessageToConversationParams) => { - const updatedConversation = await conversationsDataClient?.appendConversationMessages({ - existingConversation: conversation, - messages: messages.map((m) => ({ - ...{ - content: replaceAnonymizedValuesWithOriginalValues({ - messageContent: m.content, - replacements, - }), - role: m.role ?? 'user', - }, - timestamp: new Date().toISOString(), - })), - }); - return updatedConversation; -}; - export interface GetSystemPromptFromUserConversationParams { conversationsDataClient: AIAssistantConversationsDataClient; conversationId: string; @@ -296,23 +205,6 @@ export const appendAssistantMessageToConversation = async ({ } }; -export interface NonLangChainExecuteParams { - request: KibanaRequest; - messages: Array>; - abortSignal: AbortSignal; - actionTypeId: string; - connectorId: string; - logger: Logger; - actionsClient: PublicMethodsOf; - onLlmResponse?: ( - content: string, - traceData?: Message['traceData'], - isError?: boolean - ) => Promise; - response: KibanaResponseFactory; - telemetry: AnalyticsServiceSetup; -} - export interface LangChainExecuteParams { messages: Array>; replacements: Replacements; @@ -487,75 +379,6 @@ export const createConversationWithUserInput = async ({ } }; -export interface UpdateConversationWithParams { - logger: Logger; - conversationsDataClient: AIAssistantConversationsDataClient; - replacements: Replacements; - conversationId: string; - actionTypeId: string; - connectorId: string; - actionsClient: PublicMethodsOf; - newMessages?: Array>; - model?: string; -} -export const updateConversationWithUserInput = async ({ - logger, - conversationsDataClient, - replacements, - conversationId, - actionTypeId, - connectorId, - actionsClient, - newMessages, - model, -}: UpdateConversationWithParams) => { - const conversation = await conversationsDataClient?.getConversation({ - id: conversationId, - }); - if (conversation == null) { - throw new Error(`conversation id: "${conversationId}" not found`); - } - let updatedConversation = conversation; - - const messages = updatedConversation?.messages?.map((c) => ({ - role: c.role, - content: c.content, - timestamp: c.timestamp, - })); - - const lastMessage = newMessages?.[0] ?? messages?.[0]; - - if (conversation?.title === NEW_CHAT && lastMessage) { - const title = await generateTitleForNewChatConversation({ - message: lastMessage, - actionsClient, - actionTypeId, - connectorId, - logger, - model, - }); - const res = await conversationsDataClient.updateConversation({ - conversationUpdateProps: { - id: conversationId, - title, - }, - }); - if (res) { - updatedConversation = res; - } - } - - if (newMessages) { - return appendMessageToConversation({ - conversation: updatedConversation, - conversationsDataClient, - messages: newMessages, - replacements, - }); - } - return updatedConversation; -}; - interface PerformChecksParams { capability?: AssistantFeatureKey; context: AwaitedProperties< diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts index 1c26c6d77b53f..a5764b05c41e3 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/constants.ts @@ -5,10 +5,6 @@ * 2.0. */ -// Query for determining if ESQL docs have been loaded, searches for a specific doc. Intended for the ElasticsearchStore.similaritySearch() -// Note: We may want to add a tag of the resource name to the document metadata, so we can CRUD by specific resource -export const ESQL_DOCS_LOADED_QUERY = - 'You can chain processing commands, separated by a pipe character: `|`.'; export const SECURITY_LABS_RESOURCE = 'security_labs'; export const USER_RESOURCE = 'user'; // Query for determining if Security Labs docs have been loaded. Intended for use with Telemetry diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts index c6c5f9d94bef3..756e32883ad87 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/entries/bulk_actions_route.ts @@ -53,8 +53,6 @@ export type BulkResponse = KnowledgeBaseEntryBulkCrudActionResults & { errors?: BulkOperationError[]; }; -export type BulkActionError = BulkOperationError | unknown; - const buildBulkResponse = ( response: KibanaResponseFactory, { diff --git a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts index 5106c31d39e7d..96728f66aef7c 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/knowledge_base/get_knowledge_base_indices.ts @@ -17,7 +17,7 @@ import { buildResponse } from '../../lib/build_response'; import { ElasticAssistantPluginRouter } from '../../types'; /** - * Get the indices that have fields of `sematic_text` type + * Get the indices that have fields of `semantic_text` type * * @param router IRouter for registering routes */ diff --git a/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts index d3ee47854e7a0..79fd23c0cc843 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/prompts/bulk_actions_route.ts @@ -45,8 +45,6 @@ export interface BulkOperationError { }; } -export type BulkActionError = BulkOperationError | unknown; - const buildBulkResponse = ( response: KibanaResponseFactory, { diff --git a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts index 9c353997f1d46..29fe59cc3d4c6 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/user_conversations/bulk_actions_route.ts @@ -46,8 +46,6 @@ export interface BulkOperationError { }; } -export type BulkActionError = BulkOperationError | unknown; - const buildBulkResponse = ( response: KibanaResponseFactory, { diff --git a/x-pack/plugins/elastic_assistant/server/routes/utils.ts b/x-pack/plugins/elastic_assistant/server/routes/utils.ts index 0fb51c7364809..54f9ef2c04b90 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/utils.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/utils.ts @@ -25,10 +25,6 @@ import { } from '@kbn/stack-connectors-plugin/common/openai/constants'; import { CustomHttpRequestError } from './custom_http_request_error'; -export interface OutputError { - message: string; - statusCode: number; -} export interface BulkError { // Id can be single id or stringified ids. id?: string; diff --git a/x-pack/plugins/elastic_assistant/server/types.ts b/x-pack/plugins/elastic_assistant/server/types.ts index b021ef5a7017d..d2dad4f9f998f 100755 --- a/x-pack/plugins/elastic_assistant/server/types.ts +++ b/x-pack/plugins/elastic_assistant/server/types.ts @@ -151,13 +151,6 @@ export type ElasticAssistantPluginCoreSetupDependencies = CoreSetup< export type GetElser = () => Promise | never; -export interface InitAssistantResult { - assistantResourcesInstalled: boolean; - assistantNamespaceResourcesInstalled: boolean; - assistantSettingsCreated: boolean; - errors: string[]; -} - export interface AssistantResourceNames { componentTemplate: { conversations: string; @@ -201,18 +194,6 @@ export interface IIndexPatternString { secondaryAlias?: string; } -export interface PublicAIAssistantDataClient { - getConversationsLimitValue: () => number; -} - -export interface IAIAssistantDataClient { - client(): PublicAIAssistantDataClient | null; -} - -export interface AIAssistantPrompts { - id: string; -} - /** * Interfaces for registering tools to be used by the elastic assistant */ diff --git a/x-pack/plugins/security_solution/public/assistant/get_comments/custom_codeblock/custom_codeblock_markdown_plugin.tsx b/x-pack/plugins/security_solution/public/assistant/get_comments/custom_codeblock/custom_codeblock_markdown_plugin.tsx index 19f566537a2b6..c00224d0eae04 100644 --- a/x-pack/plugins/security_solution/public/assistant/get_comments/custom_codeblock/custom_codeblock_markdown_plugin.tsx +++ b/x-pack/plugins/security_solution/public/assistant/get_comments/custom_codeblock/custom_codeblock_markdown_plugin.tsx @@ -9,11 +9,11 @@ import type { Node } from 'unist'; import type { Parent } from 'mdast'; export const customCodeBlockLanguagePlugin = () => { - const visitor = (node: Node, parent?: Parent) => { + const visitor = (node: Node) => { if ('children' in node) { const nodeAsParent = node as Parent; nodeAsParent.children.forEach((child) => { - visitor(child, nodeAsParent); + visitor(child); }); } diff --git a/x-pack/plugins/security_solution/public/assistant/helpers.tsx b/x-pack/plugins/security_solution/public/assistant/helpers.tsx index 84d0b9ac0fb62..32672a047b27c 100644 --- a/x-pack/plugins/security_solution/public/assistant/helpers.tsx +++ b/x-pack/plugins/security_solution/public/assistant/helpers.tsx @@ -16,10 +16,6 @@ import { SendToTimelineButton } from './send_to_timeline'; import { DETECTION_RULES_CREATE_FORM_CONVERSATION_ID } from '../detections/pages/detection_engine/translations'; export const LOCAL_STORAGE_KEY = `securityAssistant`; import { UpdateQueryInFormButton } from './update_query_in_form'; -export interface QueryField { - field: string; - values: string; -} export const getPromptContextFromDetectionRules = (rules: Rule[]): string => { const data = rules.map((rule) => `Rule Name:${rule.name}\nRule Description:${rule.description}`); @@ -27,25 +23,11 @@ export const getPromptContextFromDetectionRules = (rules: Rule[]): string => { return data.join('\n\n'); }; -export const getAllFields = (data: TimelineEventsDetailsItem[]): QueryField[] => - data - .filter(({ field }) => !field.startsWith('signal.')) - .map(({ field, values }) => ({ field, values: values?.join(',') ?? '' })); - export const getRawData = (data: TimelineEventsDetailsItem[]): Record => data .filter(({ field }) => !field.startsWith('signal.')) .reduce((acc, { field, values }) => ({ ...acc, [field]: values ?? [] }), {}); -export const getFieldsAsCsv = (queryFields: QueryField[]): string => - queryFields.map(({ field, values }) => `${field},${values}`).join('\n'); - -export const getPromptContextFromEventDetailsItem = (data: TimelineEventsDetailsItem[]): string => { - const allFields = getAllFields(data); - - return getFieldsAsCsv(allFields); -}; - const sendToTimelineEligibleQueryTypes: Array = [ 'kql', 'dsl', diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/index.tsx index ab41885563954..3a529627f0902 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack/mini_attack_chain/index.tsx @@ -24,7 +24,7 @@ const MiniAttackChainComponent: React.FC = ({ attackDiscovery }) => { const detectedTacticsList = useMemo( () => - detectedTactics.map(({ name, detected }) => ( + detectedTactics.map(({ name }) => (
  • {' - '} {name} diff --git a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts index 5c5fbcdd4f6e4..55b0e8ca43349 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/attack_discovery_panel/actions/use_add_to_existing_case/translations.ts @@ -20,12 +20,3 @@ export const ADD_TO_NEW_CASE = i18n.translate( defaultMessage: 'Add to new case', } ); - -export const CREATE_A_CASE_FOR_ATTACK_DISCOVERY = (title: string) => - i18n.translate( - 'xpack.securitySolution.attackDiscovery.attackDiscoveryPanel.actions.useAddToCase.createACaseForAttackDiscoveryHeaderText', - { - values: { title }, - defaultMessage: 'Create a case for attack discovery {title}', - } - ); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts b/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts index 6c703d799d405..172c0a502b4b0 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/mock/mock_use_attack_discovery.ts @@ -197,87 +197,3 @@ export const getMockUseAttackDiscoveriesWithNoAttackDiscoveriesLoading = ( replacements: {}, isLoading: true, // <-- attack discoveries are being generated }); - -export const getRawAttackDiscoveryResponse = () => ({ - alertsContextCount: 20, - attackDiscoveries: [ - { - alertIds: [ - '382d546a7ba5ab35c050f106bece236e87e3d51076a479f0beae8b2015b8fb26', - 'ca9da6b3b77b7038d958b9e144f0a406c223a862c0c991ce9782b98e03a98c87', - '5301f4fb014538df7ce1eb9929227dde3adc0bf5b4f28aa15c8aa4e4fda95f35', - '1459af4af8b92e1710c0ee075b1c444eaa927583dfd71b42e9a10de37c8b9cf0', - '468457e9c5132aadae501b75ec5b766e1465ab865ad8d79e03f66593a76fccdf', - 'fb92e7fa5679db3e91d84d998faddb7ed269f1c8cdc40443f35e67c930383d34', - '03e0f8f1598018da8143bba6b60e6ddea30551a2286ba76d717568eed3d17a66', - '28021a7aca7de03018d820182c9784f8d5f2e1b99e0159177509a69bee1c3ac0', - ], - detailsMarkdown: - 'The following attack progression appears to have occurred on the host {{ host.name 05207978-1585-4e46-9b36-69c4bb85a768 }} involving the user {{ user.name ddc8db29-46eb-44fe-80b6-1ea642c338ac }}:\\n\\n- A suspicious application named "My Go Application.app" was launched, likely through a malicious download or installation\\n- This application attempted to run various malicious scripts and commands, including:\\n - Spawning a child process to run the "osascript" utility to display a fake system dialog prompting for user credentials ({{ process.command_line osascript -e display dialog "MacOS wants to access System Preferences\\n\\t\\t\\nPlease enter your password." with title "System Preferences" with icon file "System:Library:CoreServices:CoreTypes.bundle:Contents:Resources:ToolbarAdvanced.icns" default answer "" giving up after 30 with hidden answer ¬ }})\\n - Modifying permissions on a suspicious file named "unix1" ({{ process.command_line chmod 777 /Users/james/unix1 }})\\n - Executing the suspicious "unix1" file and passing it the user\'s login keychain file and a hardcoded password ({{ process.command_line /Users/james/unix1 /Users/james/library/Keychains/login.keychain-db TempTemp1234!! }})\\n\\nThis appears to be a multi-stage malware attack, potentially aimed at credential theft and further malicious execution on the compromised host. The tactics used align with Credential Access ({{ threat.tactic.name Credential Access }}) and Execution ({{ threat.tactic.name Execution }}) based on MITRE ATT&CK.', - entitySummaryMarkdown: - 'Suspicious activity detected on {{ host.name 05207978-1585-4e46-9b36-69c4bb85a768 }} involving {{ user.name ddc8db29-46eb-44fe-80b6-1ea642c338ac }}.', - mitreAttackTactics: ['Credential Access', 'Execution'], - summaryMarkdown: - 'A multi-stage malware attack was detected on a macOS host, likely initiated through a malicious application download. The attack involved credential phishing attempts, suspicious file modifications, and the execution of untrusted binaries potentially aimed at credential theft. {{ host.name 05207978-1585-4e46-9b36-69c4bb85a768 }} and {{ user.name ddc8db29-46eb-44fe-80b6-1ea642c338ac }} were involved.', - title: 'Credential Theft Malware Attack on macOS', - }, - { - alertIds: [ - '8772effc4970e371a26d556556f68cb8c73f9d9d9482b7f20ee1b1710e642a23', - '63c761718211fa51ea797669d845c3d4f23b1a28c77a101536905e6fd0b4aaa6', - '55f4641a9604e1088deae4897e346e63108bde9167256c7cb236164233899dcc', - 'eaf9991c83feef7798983dc7cacda86717d77136a3a72c9122178a03ce2f15d1', - 'f7044f707ac119256e5a0ccd41d451b51bca00bdc6899c7e5e8e1edddfeb6774', - 'fad83b4223f3c159646ad22df9877b9c400f9472655e49781e2a5951b641088e', - ], - detailsMarkdown: - 'The following attack progression appears to have occurred on the host {{ host.name b775910b-4b71-494d-bfb1-4be3fe88c2b0 }} involving the user {{ user.name e411fe2e-aeea-44b5-b09a-4336dabb3969 }}:\\n\\n- A malicious Microsoft Office document was opened, spawning a child process to write a suspicious VBScript file named "AppPool.vbs" ({{ file.path C:\\ProgramData\\WindowsAppPool\\AppPool.vbs }})\\n- The VBScript launched PowerShell and executed an obfuscated script from "AppPool.ps1"\\n- Additional malicious activities were performed, including:\\n - Creating a scheduled task to periodically execute the VBScript\\n - Spawning a cmd.exe process to create the scheduled task\\n - Executing the VBScript directly\\n\\nThis appears to be a multi-stage malware attack initiated through malicious Office documents, employing script obfuscation, scheduled task persistence, and defense evasion tactics. The activities map to Initial Access ({{ threat.tactic.name Initial Access }}), Execution ({{ threat.tactic.name Execution }}), and Defense Evasion ({{ threat.tactic.name Defense Evasion }}) based on MITRE ATT&CK.', - entitySummaryMarkdown: - 'Suspicious activity detected on {{ host.name b775910b-4b71-494d-bfb1-4be3fe88c2b0 }} involving {{ user.name e411fe2e-aeea-44b5-b09a-4336dabb3969 }}.', - mitreAttackTactics: ['Initial Access', 'Execution', 'Defense Evasion'], - summaryMarkdown: - 'A multi-stage malware attack was detected on a Windows host, likely initiated through a malicious Microsoft Office document. The attack involved script obfuscation, scheduled task persistence, and other defense evasion tactics. {{ host.name b775910b-4b71-494d-bfb1-4be3fe88c2b0 }} and {{ user.name e411fe2e-aeea-44b5-b09a-4336dabb3969 }} were involved.', - title: 'Malicious Office Document Initiates Malware Attack', - }, - { - alertIds: [ - 'd1b8b1c6f891fd181af236d0a81b8769c4569016d5b341cdf6a3fefb7cf9cbfd', - '005f2dfb7efb08b34865b308876ecad188fc9a3eebf35b5e3af3c3780a3fb239', - '7e41ddd221831544c5ff805e0ec31fc3c1f22c04257de1366112cfef14df9f63', - ], - detailsMarkdown: - 'The following attack progression appears to have occurred on the host {{ host.name c1e00157-c636-4222-b3a2-5d9ea667a3a8 }} involving the user {{ user.name e411fe2e-aeea-44b5-b09a-4336dabb3969 }}:\\n\\n- A suspicious process launched by msiexec.exe spawned a PowerShell session\\n- The PowerShell process exhibited the following malicious behaviors:\\n - Shellcode injection detected, indicating the presence of the "Windows.Trojan.Bumblebee" malware\\n - Establishing network connections, suggesting command and control or data exfiltration\\n\\nThis appears to be a case of malware delivery and execution via an MSI package, potentially initiated through a software supply chain compromise or social engineering attack. The tactics employed align with Defense Evasion ({{ threat.tactic.name Defense Evasion }}) through system binary proxy execution, as well as potential Command and Control ({{ threat.tactic.name Command and Control }}) based on MITRE ATT&CK.', - entitySummaryMarkdown: - 'Suspicious activity detected on {{ host.name c1e00157-c636-4222-b3a2-5d9ea667a3a8 }} involving {{ user.name e411fe2e-aeea-44b5-b09a-4336dabb3969 }}.', - mitreAttackTactics: ['Defense Evasion', 'Command and Control'], - summaryMarkdown: - 'A malware attack was detected on a Windows host, likely delivered through a compromised MSI package. The attack involved shellcode injection, network connections, and the use of system binaries for defense evasion. {{ host.name c1e00157-c636-4222-b3a2-5d9ea667a3a8 }} and {{ user.name e411fe2e-aeea-44b5-b09a-4336dabb3969 }} were involved.', - title: 'Malware Delivery via Compromised MSI Package', - }, - { - alertIds: [ - '12057d82e79068080f6acf268ca45c777d3f80946b466b59954320ec5f86f24a', - '81c7c57a360bee531b1398b0773e7c4a2332fbdda4e66f135e01fc98ec7f4e3d', - ], - detailsMarkdown: - 'The following attack progression appears to have occurred on the host {{ host.name d4c92b0d-b82f-4702-892d-dd06ad8418e8 }} involving the user {{ user.name 7245f867-9a09-48d7-9165-84a69fa0727d }}:\\n\\n- A malicious file named "kdmtmpflush" with the SHA256 hash {{ file.hash.sha256 74ef6cc38f5a1a80148752b63c117e6846984debd2af806c65887195a8eccc56 }} was copied to the /dev/shm directory\\n- Permissions were modified to make the file executable\\n- The file was then executed with the "--init" argument, likely to initialize malicious components\\n\\nThis appears to be a case of the "Linux.Trojan.BPFDoor" malware being deployed on the Linux host. The tactics employed align with Execution ({{ threat.tactic.name Execution }}) based on MITRE ATT&CK.', - entitySummaryMarkdown: - 'Suspicious activity detected on {{ host.name d4c92b0d-b82f-4702-892d-dd06ad8418e8 }} involving {{ user.name 7245f867-9a09-48d7-9165-84a69fa0727d }}.', - mitreAttackTactics: ['Execution'], - summaryMarkdown: - 'The "Linux.Trojan.BPFDoor" malware was detected being deployed on a Linux host. A malicious file was copied, permissions were modified, and the file was executed to likely initialize malicious components. {{ host.name d4c92b0d-b82f-4702-892d-dd06ad8418e8 }} and {{ user.name 7245f867-9a09-48d7-9165-84a69fa0727d }} were involved.', - title: 'Linux.Trojan.BPFDoor Malware Deployment Detected', - }, - ], - connector_id: 'pmeClaudeV3SonnetUsEast1', - replacements: { - 'ddc8db29-46eb-44fe-80b6-1ea642c338ac': 'james', - '05207978-1585-4e46-9b36-69c4bb85a768': 'SRVMAC08', - '7245f867-9a09-48d7-9165-84a69fa0727d': 'root', - 'e411fe2e-aeea-44b5-b09a-4336dabb3969': 'Administrator', - '5a63f6dc-4e40-41fe-a92c-7898e891025e': 'SRVWIN07-PRIV', - 'b775910b-4b71-494d-bfb1-4be3fe88c2b0': 'SRVWIN07', - 'c1e00157-c636-4222-b3a2-5d9ea667a3a8': 'SRVWIN06', - 'd4c92b0d-b82f-4702-892d-dd06ad8418e8': 'SRVNIX05', - }, -}); diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/header/settings_modal/alerts_settings/index.tsx b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/settings_modal/alerts_settings/index.tsx index 336da549f55ea..7741d3214ee36 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/header/settings_modal/alerts_settings/index.tsx +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/header/settings_modal/alerts_settings/index.tsx @@ -18,7 +18,6 @@ import * as i18n from '../translations'; export const MAX_ALERTS = 500; export const MIN_ALERTS = 50; -export const ROW_MIN_WITH = 550; // px export const STEP = 50; interface Props { diff --git a/x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts b/x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts index b990c3ccf1555..6f07136b54773 100644 --- a/x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts +++ b/x-pack/plugins/security_solution/public/attack_discovery/pages/helpers.ts @@ -7,9 +7,6 @@ export const getInitialIsOpen = (index: number) => index < 3; -export const getFallbackActionTypeId = (actionTypeId: string | undefined): string => - actionTypeId != null ? actionTypeId : '.gen-ai'; - interface ErrorWithStringMessage { body?: { error?: string; @@ -50,10 +47,6 @@ export function isErrorWithStructuredMessage(error: any): error is ErrorWithStru export const CONNECTOR_ID_LOCAL_STORAGE_KEY = 'connectorId'; -export const CACHED_ATTACK_DISCOVERIES_SESSION_STORAGE_KEY = 'cachedAttackDiscoveries'; - -export const GENERATION_INTERVALS_LOCAL_STORAGE_KEY = 'generationIntervals'; - export const getErrorToastText = ( error: ErrorWithStringMessage | ErrorWithStructuredMessage | unknown ): string => { diff --git a/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts index cea2bdadf5970..4369f85a83c25 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts @@ -40,7 +40,7 @@ export const KNOWLEDGE_BASE_RETRIEVAL_TOOL: AssistantTool = { schema: z.object({ query: z.string().describe(`Summary of items/things to search for in the knowledge base`), }), - func: async (input, _, cbManager) => { + func: async (input) => { logger.debug( () => `KnowledgeBaseRetrievalToolParams:input\n ${JSON.stringify(input, null, 2)}` ); diff --git a/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts index c46e6a364b873..950a22c635036 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts @@ -53,7 +53,7 @@ export const KNOWLEDGE_BASE_WRITE_TOOL: AssistantTool = { ) .default(false), }), - func: async (input, _, cbManager) => { + func: async (input) => { logger.debug( () => `KnowledgeBaseWriteToolParams:input\n ${JSON.stringify(input, null, 2)}` ); diff --git a/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_attack_discovery_chain_result.ts b/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_attack_discovery_chain_result.ts deleted file mode 100644 index 7a859a093f432..0000000000000 --- a/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_attack_discovery_chain_result.ts +++ /dev/null @@ -1,64 +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. - */ - -export const mockAttackDiscoveryChainResult = { - records: [ - { - alertIds: [ - 'b6e883c29b32571aaa667fa13e65bbb4f95172a2b84bdfb85d6f16c72b2d2560', - '0215a6c5cc9499dd0290cd69a4947efb87d3ddd8b6385a766d122c2475be7367', - '600eb9eca925f4c5b544b4e9d3cf95d83b7829f8f74c5bd746369cb4c2968b9a', - 'e1f4a4ed70190eb4bd256c813029a6a9101575887cdbfa226ac330fbd3063f0c', - '2a7a4809ca625dfe22ccd35fbef7a7ba8ed07f109e5cbd17250755cfb0bc615f', - ], - detailsMarkdown: - '- Malicious Go application named "My Go Application.app" is being executed from temporary directories, likely indicating malware delivery\n- The malicious application is spawning child processes like `osascript` to display fake system dialogs and attempt to phish user credentials ({{ host.name 6c57a4f7-b30b-465d-a670-47377655b1bb }}, {{ user.name 639fab6d-369b-4879-beae-7767a7145c7f }})\n- The malicious application is also executing `chmod` to make the file `unix1` executable ({{ file.path /Users/james/unix1 }})\n- `unix1` is a potentially malicious executable that is being run with suspicious arguments related to the macOS keychain ({{ process.command_line /Users/james/unix1 /Users/james/library/Keychains/login.keychain-db TempTemp1234!! }})\n- Multiple detections indicate the presence of malware on the host attempting credential access and execution of malicious payloads', - entitySummaryMarkdown: - 'Malicious activity detected on {{ host.name 6c57a4f7-b30b-465d-a670-47377655b1bb }} involving user {{ user.name 639fab6d-369b-4879-beae-7767a7145c7f }}.', - mitreAttackTactics: ['Credential Access', 'Execution'], - summaryMarkdown: - 'Multiple detections indicate the presence of malware on a macOS host {{ host.name 6c57a4f7-b30b-465d-a670-47377655b1bb }} attempting credential theft and execution of malicious payloads targeting the user {{ user.name 639fab6d-369b-4879-beae-7767a7145c7f }}.', - title: 'Malware Delivering Malicious Payloads on macOS', - }, - { - alertIds: [ - 'f465ca9fbfc8bc3b1871e965c9e111cac76ff3f4076fed6bc9da88d49fb43014', - 'ce110da958fe0cf0c07599a21c68d90a64c93b7607aa27970a614c7f49598316', - 'dd9e4ea23961ccfdb7a9c760ee6bedd19a013beac3b0d38227e7ae77ba4ce515', - 'f30d55e503b1d848b34ee57741b203d8052360dd873ea34802f3fa7a9ef34d0a', - '6f8cd5e8021dbb64598f2b7ec56bee21fd00d1e62d4e08905f86bf234873ee66', - 'aa283e6a13be77b533eceffb09e48254c8f91feeccc39f7eed80fd3881d053f4', - '7b4f49f21cf141e67856d3207fb4ea069c8035b41f0ea501970694cf8bd43cbe', - 'ea81d79104cbd442236b5bcdb7a3331de897aa4ce1523e622068038d048d0a9e', - '0866787b0027b4d908767ac16e35a1da00970c83632ba85be65f2ad371132b4f', - 'b0fdf96721e361e1137d49a67e26d92f96b146392d7f44322bddc3d660abaef1', - ], - detailsMarkdown: - '- A malicious executable named `d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe` is being executed from `C:\\Users\\Administrator\\Desktop\\8813719803\\` ({{ file.path C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe }})\n- The malicious executable is injecting shellcode into the legitimate Windows process `MsMpEng.exe` ({{ process.name MsMpEng.exe }})\n- Signatures indicate the shellcode is related to ransomware\n- The malicious executable is also loading and manipulating the Windows library `mpsvc.dll` ({{ file.path C:\\Windows\\mpsvc.dll }})\n- Ransomware artifacts like text files with the extension `.txt` are being created, indicating potential ransomware execution ({{ Ransomware.files.path c:\\hd3vuk19y-readme.txt }})\n- The activity is occurring for the user `f02a851c-9e18-4501-97d3-61d1b0c4c55b` on the host `61af21b2-33ff-4a78-81a1-40fb979da0bb`', - entitySummaryMarkdown: - 'Ransomware activity detected on {{ host.name 61af21b2-33ff-4a78-81a1-40fb979da0bb }} involving user {{ user.name f02a851c-9e18-4501-97d3-61d1b0c4c55b }}.', - mitreAttackTactics: ['Execution', 'Defense Evasion'], - summaryMarkdown: - 'Ransomware has been detected executing on the Windows host {{ host.name 61af21b2-33ff-4a78-81a1-40fb979da0bb }} and impacting the user {{ user.name f02a851c-9e18-4501-97d3-61d1b0c4c55b }}. The malware is injecting shellcode, loading malicious libraries, and creating ransomware artifacts.', - title: 'Ransomware Executing on Windows Host', - }, - { - alertIds: [ - 'cdf3b5510bb5ed622e8cefd1ce6bedc52bdd99a4c1ead537af0603469e713c8b', - '6abe81eb6350fb08031761be029e7ab19f7e577a7c17a9c5ea1ed010ba1620e3', - ], - detailsMarkdown: - '- A malicious DLL named `cdnver.dll` is being loaded by the Windows process `rundll32.exe` with suspicious arguments ({{ process.command_line "C:\\Windows\\System32\\rundll32.exe" "C:\\Users\\Administrator\\AppData\\Local\\cdnver.dll",#1 }})\n- The malicious DLL is likely being used for execution of malicious code on the host `feb0c555-7572-4427-9475-2052d15373f9`\n- The activity is occurring for the user `f02a851c-9e18-4501-97d3-61d1b0c4c55b`', - entitySummaryMarkdown: - 'Malicious DLL execution detected on {{ host.name feb0c555-7572-4427-9475-2052d15373f9 }} involving user {{ user.name f02a851c-9e18-4501-97d3-61d1b0c4c55b }}.', - mitreAttackTactics: ['Defense Evasion', 'Execution'], - summaryMarkdown: - 'A malicious DLL named `cdnver.dll` is being loaded by `rundll32.exe` on the Windows host {{ host.name feb0c555-7572-4427-9475-2052d15373f9 }} likely for execution of malicious code. The activity involves the user {{ user.name f02a851c-9e18-4501-97d3-61d1b0c4c55b }}.', - title: 'Malicious DLL Loaded via Rundll32 on Windows', - }, - ], -}; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts b/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts deleted file mode 100644 index ed5549acc586a..0000000000000 --- a/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_empty_open_and_acknowledged_alerts_qery_results.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const mockEmptyOpenAndAcknowledgedAlertsQueryResults = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 0, - relation: 'eq', - }, - max_score: null, - hits: [], - }, -}; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_open_and_acknowledged_alerts_query_results.ts b/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_open_and_acknowledged_alerts_query_results.ts deleted file mode 100644 index 3f22f787f54f8..0000000000000 --- a/x-pack/plugins/security_solution/server/assistant/tools/mock/mock_open_and_acknowledged_alerts_query_results.ts +++ /dev/null @@ -1,1396 +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. - */ - -export const mockOpenAndAcknowledgedAlertsQueryResults = { - took: 13, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 31, - relation: 'eq', - }, - max_score: null, - hits: [ - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'b6e883c29b32571aaa667fa13e65bbb4f95172a2b84bdfb85d6f16c72b2d2560', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['/Users/james/unix1'], - 'process.hash.md5': ['85caafe3d324e3287b85348fa2fae492'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': [ - '/Users/james/unix1 /Users/james/library/Keychains/login.keychain-db TempTemp1234!!', - ], - 'process.parent.name': ['unix1'], - 'user.name': ['james'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '0b18d6880dc9670ab2b955914598c96fc3d0097dc40ea61157b8c79e75edf231', - ], - 'process.code_signature.signing_id': ['nans-55554944e5f232edcf023cf68e8e5dac81584f78'], - 'process.pid': [1227], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': [ - 'code failed to satisfy specified code requirement(s)', - ], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': [''], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['0b18d6880dc9670ab2b955914598c96fc3d0097dc40ea61157b8c79e75edf231'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.72442], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': ['/Users/james/unix1'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.parent.code_signature.subject_name': [''], - 'process.parent.executable': ['/Users/james/unix1'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['unix1'], - 'process.args': [ - '/Users/james/unix1', - '/Users/james/library/Keychains/login.keychain-db', - 'TempTemp1234!!', - ], - 'process.code_signature.status': ['code failed to satisfy specified code requirement(s)'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [3], - 'process.name': ['unix1'], - 'process.parent.args': [ - '/Users/james/unix1', - '/Users/james/library/Keychains/login.keychain-db', - 'TempTemp1234!!', - ], - '@timestamp': ['2024-05-07T12:48:45.032Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': [ - '/Users/james/unix1 /Users/james/library/Keychains/login.keychain-db TempTemp1234!!', - ], - 'host.risk.calculated_level': ['High'], - _id: ['b6e883c29b32571aaa667fa13e65bbb4f95172a2b84bdfb85d6f16c72b2d2560'], - 'process.hash.sha1': ['4ca549355736e4af6434efc4ec9a044ceb2ae3c3'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:28:39.368Z'], - }, - sort: [99, 1715086125032], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '0215a6c5cc9499dd0290cd69a4947efb87d3ddd8b6385a766d122c2475be7367', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['/Users/james/unix1'], - 'process.hash.md5': ['e62bdd3eaf2be436fca2e67b7eede603'], - 'event.category': ['malware', 'intrusion_detection', 'file'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.parent.name': ['My Go Application.app'], - 'user.name': ['james'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097', - ], - 'process.code_signature.signing_id': ['a.out'], - 'process.pid': [1220], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': [ - 'code failed to satisfy specified code requirement(s)', - ], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': [''], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['0b18d6880dc9670ab2b955914598c96fc3d0097dc40ea61157b8c79e75edf231'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.72442], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.parent.code_signature.subject_name': [''], - 'process.parent.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['unix1'], - 'process.args': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.code_signature.status': ['code failed to satisfy specified code requirement(s)'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['My Go Application.app'], - 'process.parent.args': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - '@timestamp': ['2024-05-07T12:48:45.030Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'host.risk.calculated_level': ['High'], - _id: ['0215a6c5cc9499dd0290cd69a4947efb87d3ddd8b6385a766d122c2475be7367'], - 'process.hash.sha1': ['58a3bddbc7c45193ecbefa22ad0496b60a29dff2'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:28:38.061Z'], - }, - sort: [99, 1715086125030], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '600eb9eca925f4c5b544b4e9d3cf95d83b7829f8f74c5bd746369cb4c2968b9a', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['/Users/james/unix1'], - 'process.hash.md5': ['85caafe3d324e3287b85348fa2fae492'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.parent.name': ['My Go Application.app'], - 'user.name': ['james'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '0b18d6880dc9670ab2b955914598c96fc3d0097dc40ea61157b8c79e75edf231', - ], - 'process.code_signature.signing_id': ['nans-55554944e5f232edcf023cf68e8e5dac81584f78'], - 'process.pid': [1220], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': [ - 'code failed to satisfy specified code requirement(s)', - ], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': [''], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['0b18d6880dc9670ab2b955914598c96fc3d0097dc40ea61157b8c79e75edf231'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.72442], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': ['/Users/james/unix1'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.parent.code_signature.subject_name': [''], - 'process.parent.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['unix1'], - 'process.args': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.code_signature.status': ['code failed to satisfy specified code requirement(s)'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['unix1'], - 'process.parent.args': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - '@timestamp': ['2024-05-07T12:48:45.029Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'host.risk.calculated_level': ['High'], - _id: ['600eb9eca925f4c5b544b4e9d3cf95d83b7829f8f74c5bd746369cb4c2968b9a'], - 'process.hash.sha1': ['4ca549355736e4af6434efc4ec9a044ceb2ae3c3'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:28:37.881Z'], - }, - sort: [99, 1715086125029], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'e1f4a4ed70190eb4bd256c813029a6a9101575887cdbfa226ac330fbd3063f0c', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['/Users/james/unix1'], - 'process.hash.md5': ['3f19892ab44eb9bc7bc03f438944301e'], - 'event.category': ['malware', 'intrusion_detection', 'file'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.parent.name': ['My Go Application.app'], - 'user.name': ['james'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - 'f80234ff6fed2c62d23f37443f2412fbe806711b6add2ac126e03e282082c8f5', - ], - 'process.code_signature.signing_id': ['com.apple.chmod'], - 'process.pid': [1219], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': [ - 'code failed to satisfy specified code requirement(s)', - ], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Software Signing'], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['0b18d6880dc9670ab2b955914598c96fc3d0097dc40ea61157b8c79e75edf231'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.72442], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': ['/bin/chmod'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'process.parent.code_signature.subject_name': [''], - 'process.parent.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['unix1'], - 'process.args': ['chmod', '777', '/Users/james/unix1'], - 'process.code_signature.status': ['No error.'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['chmod'], - 'process.parent.args': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - '@timestamp': ['2024-05-07T12:48:45.028Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': ['chmod 777 /Users/james/unix1'], - 'host.risk.calculated_level': ['High'], - _id: ['e1f4a4ed70190eb4bd256c813029a6a9101575887cdbfa226ac330fbd3063f0c'], - 'process.hash.sha1': ['217490d4f51717aa3b301abec96be08602370d2d'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:28:37.869Z'], - }, - sort: [99, 1715086125028], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '2a7a4809ca625dfe22ccd35fbef7a7ba8ed07f109e5cbd17250755cfb0bc615f', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['643dddff1a57cbf70594854b44eb1a1d'], - 'event.category': ['malware', 'intrusion_detection'], - 'host.risk.calculated_score_norm': [73.02488], - 'rule.reference': [ - 'https://github.com/EmpireProject/EmPyre/blob/master/lib/modules/collection/osx/prompt.py', - 'https://ss64.com/osx/osascript.html', - ], - 'process.parent.name': ['My Go Application.app'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - 'bab17feba710b469e5d96820f0cb7ed511d983e5817f374ec3cb46462ac5b794', - ], - 'process.pid': [1206], - 'process.code_signature.exists': [true], - 'process.code_signature.subject_name': ['Software Signing'], - 'host.os.version': ['13.4'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.72442], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': [ - 'Malicious Behavior Detection Alert: Potential Credentials Phishing via OSASCRIPT', - ], - 'host.name': ['SRVMAC08'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'group.name': ['staff'], - 'kibana.alert.workflow_status': ['open'], - 'rule.name': ['Potential Credentials Phishing via OSASCRIPT'], - 'threat.tactic.id': ['TA0006'], - 'threat.tactic.name': ['Credential Access'], - 'threat.technique.id': ['T1056'], - 'process.parent.args_count': [0], - 'threat.technique.subtechnique.reference': [ - 'https://attack.mitre.org/techniques/T1056/002/', - ], - 'process.name': ['osascript'], - 'threat.technique.subtechnique.name': ['GUI Input Capture'], - 'process.parent.code_signature.trusted': [false], - _id: ['2a7a4809ca625dfe22ccd35fbef7a7ba8ed07f109e5cbd17250755cfb0bc615f'], - 'threat.technique.name': ['Input Capture'], - 'group.id': ['20'], - 'threat.tactic.reference': ['https://attack.mitre.org/tactics/TA0006/'], - 'user.name': ['james'], - 'threat.framework': ['MITRE ATT&CK'], - 'process.code_signature.signing_id': ['com.apple.osascript'], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': [ - 'code failed to satisfy specified code requirement(s)', - ], - 'event.module': ['endpoint'], - 'process.executable': ['/usr/bin/osascript'], - 'process.parent.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.args': [ - 'osascript', - '-e', - 'display dialog "MacOS wants to access System Preferences\n\t\t\nPlease enter your password." with title "System Preferences" with icon file "System:Library:CoreServices:CoreTypes.bundle:Contents:Resources:ToolbarAdvanced.icns" default answer "" giving up after 30 with hidden answer ¬', - ], - 'process.code_signature.status': ['No error.'], - message: [ - 'Malicious Behavior Detection Alert: Potential Credentials Phishing via OSASCRIPT', - ], - '@timestamp': ['2024-05-07T12:48:45.027Z'], - 'threat.technique.subtechnique.id': ['T1056.002'], - 'threat.technique.reference': ['https://attack.mitre.org/techniques/T1056/'], - 'process.command_line': [ - 'osascript -e display dialog "MacOS wants to access System Preferences\n\t\t\nPlease enter your password." with title "System Preferences" with icon file "System:Library:CoreServices:CoreTypes.bundle:Contents:Resources:ToolbarAdvanced.icns" default answer "" giving up after 30 with hidden answer ¬', - ], - 'host.risk.calculated_level': ['High'], - 'process.hash.sha1': ['0568baae15c752208ae56d8f9c737976d6de2e3a'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:28:09.909Z'], - }, - sort: [99, 1715086125027], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '2a9f7602de8656d30dda0ddcf79e78037ac2929780e13d5b2047b3bedc40bb69', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.hash.md5': ['e62bdd3eaf2be436fca2e67b7eede603'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': ['/sbin/launchd'], - 'process.parent.name': ['launchd'], - 'user.name': ['root'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097', - ], - 'process.code_signature.signing_id': ['a.out'], - 'process.pid': [1200], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['No error.'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': [''], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.491455], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/6D63F08A-011C-4511-8556-EAEF9AFD6340/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.parent.code_signature.subject_name': ['Software Signing'], - 'process.parent.executable': ['/sbin/launchd'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['My Go Application.app'], - 'process.args': ['xpcproxy', 'application.Appify by Machine Box.My Go Application.20.23'], - 'process.code_signature.status': ['code failed to satisfy specified code requirement(s)'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['My Go Application.app'], - 'process.parent.args': ['/sbin/launchd'], - '@timestamp': ['2024-05-07T12:48:45.023Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': [ - 'xpcproxy application.Appify by Machine Box.My Go Application.20.23', - ], - 'host.risk.calculated_level': ['High'], - _id: ['2a9f7602de8656d30dda0ddcf79e78037ac2929780e13d5b2047b3bedc40bb69'], - 'process.hash.sha1': ['58a3bddbc7c45193ecbefa22ad0496b60a29dff2'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:28:06.888Z'], - }, - sort: [99, 1715086125023], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '4615c3a90e8057ae5cc9b358bbbf4298e346277a2f068dda052b0b43ef6d5bbd', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/3C4D44B9-4838-4613-BACC-BD00A9CE4025/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.hash.md5': ['e62bdd3eaf2be436fca2e67b7eede603'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': ['/sbin/launchd'], - 'process.parent.name': ['launchd'], - 'user.name': ['root'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097', - ], - 'process.code_signature.signing_id': ['a.out'], - 'process.pid': [1169], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['No error.'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': [''], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.491455], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/3C4D44B9-4838-4613-BACC-BD00A9CE4025/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.parent.code_signature.subject_name': ['Software Signing'], - 'process.parent.executable': ['/sbin/launchd'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['My Go Application.app'], - 'process.args': ['xpcproxy', 'application.Appify by Machine Box.My Go Application.20.23'], - 'process.code_signature.status': ['code failed to satisfy specified code requirement(s)'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['My Go Application.app'], - 'process.parent.args': ['/sbin/launchd'], - '@timestamp': ['2024-05-07T12:48:45.022Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': [ - 'xpcproxy application.Appify by Machine Box.My Go Application.20.23', - ], - 'host.risk.calculated_level': ['High'], - _id: ['4615c3a90e8057ae5cc9b358bbbf4298e346277a2f068dda052b0b43ef6d5bbd'], - 'process.hash.sha1': ['58a3bddbc7c45193ecbefa22ad0496b60a29dff2'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:27:47.362Z'], - }, - sort: [99, 1715086125022], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '449322a72d3f19efbdf983935a1bdd21ebd6b9c761ce31e8b252003017d7e5db', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/37D933EC-334D-410A-A741-0F730D6AE3FD/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'process.hash.md5': ['e62bdd3eaf2be436fca2e67b7eede603'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': ['/sbin/launchd'], - 'process.parent.name': ['launchd'], - 'user.name': ['root'], - 'user.risk.calculated_level': ['Moderate'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097', - ], - 'process.code_signature.signing_id': ['a.out'], - 'process.pid': [1123], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['No error.'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': [''], - 'host.os.version': ['13.4'], - 'file.hash.sha256': ['2c63ba2b1a5131b80e567b7a1a93997a2de07ea20d0a8f5149701c67b832c097'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [66.491455], - 'host.os.name': ['macOS'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVMAC08'], - 'process.executable': [ - '/private/var/folders/_b/rmcpc65j6nv11ygrs50ctcjr0000gn/T/AppTranslocation/37D933EC-334D-410A-A741-0F730D6AE3FD/d/Setup.app/Contents/MacOS/My Go Application.app', - ], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.parent.code_signature.subject_name': ['Software Signing'], - 'process.parent.executable': ['/sbin/launchd'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['My Go Application.app'], - 'process.args': ['xpcproxy', 'application.Appify by Machine Box.My Go Application.20.23'], - 'process.code_signature.status': ['code failed to satisfy specified code requirement(s)'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['My Go Application.app'], - 'process.parent.args': ['/sbin/launchd'], - '@timestamp': ['2024-05-07T12:48:45.020Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': [ - 'xpcproxy application.Appify by Machine Box.My Go Application.20.23', - ], - 'host.risk.calculated_level': ['High'], - _id: ['449322a72d3f19efbdf983935a1bdd21ebd6b9c761ce31e8b252003017d7e5db'], - 'process.hash.sha1': ['58a3bddbc7c45193ecbefa22ad0496b60a29dff2'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-06-19T00:25:24.716Z'], - }, - sort: [99, 1715086125020], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'f465ca9fbfc8bc3b1871e965c9e111cac76ff3f4076fed6bc9da88d49fb43014', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['8cc83221870dd07144e63df594c391d9'], - 'event.category': ['malware', 'intrusion_detection'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'process.parent.name': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '33bc14d231a4afaa18f06513766d5f69d8b88f1e697cd127d24fb4b72ad44c7a', - ], - 'process.pid': [8708], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['errorExpired'], - 'process.pe.original_file_name': ['MsMpEng.exe'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Corporation'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Memory Threat Detection Alert: Shellcode Injection'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'process.parent.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'kibana.alert.workflow_status': ['open'], - 'process.args': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.status': ['trusted'], - message: ['Memory Threat Detection Alert: Shellcode Injection'], - 'process.parent.args_count': [1], - 'process.name': ['MsMpEng.exe'], - 'process.parent.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - '@timestamp': ['2024-05-07T12:48:45.017Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': ['"C:\\Windows\\MsMpEng.exe"'], - 'host.risk.calculated_level': ['High'], - _id: ['f465ca9fbfc8bc3b1871e965c9e111cac76ff3f4076fed6bc9da88d49fb43014'], - 'process.hash.sha1': ['3d409b39b8502fcd23335a878f2cbdaf6d721995'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:22.051Z'], - }, - sort: [99, 1715086125017], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'aa283e6a13be77b533eceffb09e48254c8f91feeccc39f7eed80fd3881d053f4', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['C:\\Windows\\mpsvc.dll'], - 'process.hash.md5': ['8cc83221870dd07144e63df594c391d9'], - 'event.category': ['malware', 'intrusion_detection', 'library'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'process.parent.name': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '33bc14d231a4afaa18f06513766d5f69d8b88f1e697cd127d24fb4b72ad44c7a', - ], - 'process.pid': [8708], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['errorExpired'], - 'process.pe.original_file_name': ['MsMpEng.exe'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Corporation'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'file.hash.sha256': ['8dd620d9aeb35960bb766458c8890ede987c33d239cf730f93fe49d90ae759dd'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\MsMpEng.exe'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'process.parent.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['mpsvc.dll'], - 'process.args': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.status': ['trusted'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['MsMpEng.exe'], - 'process.parent.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - '@timestamp': ['2024-05-07T12:48:45.008Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': ['"C:\\Windows\\MsMpEng.exe"'], - 'host.risk.calculated_level': ['High'], - _id: ['aa283e6a13be77b533eceffb09e48254c8f91feeccc39f7eed80fd3881d053f4'], - 'process.hash.sha1': ['3d409b39b8502fcd23335a878f2cbdaf6d721995'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:18.093Z'], - }, - sort: [99, 1715086125008], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'dd9e4ea23961ccfdb7a9c760ee6bedd19a013beac3b0d38227e7ae77ba4ce515', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['C:\\Windows\\mpsvc.dll'], - 'process.hash.md5': ['561cffbaba71a6e8cc1cdceda990ead4'], - 'event.category': ['malware', 'intrusion_detection', 'file'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': ['C:\\Windows\\Explorer.EXE'], - 'process.parent.name': ['explorer.exe'], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e', - ], - 'process.pid': [1008], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['trusted'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'file.hash.sha256': ['8dd620d9aeb35960bb766458c8890ede987c33d239cf730f93fe49d90ae759dd'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['Microsoft Windows'], - 'process.parent.executable': ['C:\\Windows\\explorer.exe'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['mpsvc.dll'], - 'process.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'process.code_signature.status': ['errorExpired'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe'], - 'process.parent.args': ['C:\\Windows\\Explorer.EXE'], - '@timestamp': ['2024-05-07T12:48:45.007Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'host.risk.calculated_level': ['High'], - _id: ['dd9e4ea23961ccfdb7a9c760ee6bedd19a013beac3b0d38227e7ae77ba4ce515'], - 'process.hash.sha1': ['5162f14d75e96edb914d1756349d6e11583db0b0'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:17.887Z'], - }, - sort: [99, 1715086125007], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'f30d55e503b1d848b34ee57741b203d8052360dd873ea34802f3fa7a9ef34d0a', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'process.hash.md5': ['561cffbaba71a6e8cc1cdceda990ead4'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': ['C:\\Windows\\Explorer.EXE'], - 'process.parent.name': ['explorer.exe'], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e', - ], - 'process.pid': [1008], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['trusted'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'file.hash.sha256': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [false], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['Microsoft Windows'], - 'process.parent.executable': ['C:\\Windows\\explorer.exe'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe'], - 'process.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'process.code_signature.status': ['errorExpired'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe'], - 'process.parent.args': ['C:\\Windows\\Explorer.EXE'], - '@timestamp': ['2024-05-07T12:48:45.006Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'host.risk.calculated_level': ['High'], - _id: ['f30d55e503b1d848b34ee57741b203d8052360dd873ea34802f3fa7a9ef34d0a'], - 'process.hash.sha1': ['5162f14d75e96edb914d1756349d6e11583db0b0'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:17.544Z'], - }, - sort: [99, 1715086125006], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '6f8cd5e8021dbb64598f2b7ec56bee21fd00d1e62d4e08905f86bf234873ee66', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'process.hash.md5': ['f070b5cf25febb9a88a168efd87c6112'], - 'event.category': ['malware', 'intrusion_detection', 'file'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [''], - 'process.parent.name': ['userinit.exe'], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '567be4d1e15f4ff96d92e7d28e191076f5813f50be96bf4c3916e4ecf53f66cd', - ], - 'process.pid': [6228], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['trusted'], - 'process.pe.original_file_name': ['EXPLORER.EXE'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Windows'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'file.hash.sha256': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\explorer.exe'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['Microsoft Windows'], - 'process.parent.executable': ['C:\\Windows\\System32\\userinit.exe'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe'], - 'process.args': ['C:\\Windows\\Explorer.EXE'], - 'process.code_signature.status': ['trusted'], - message: ['Malware Detection Alert'], - 'process.name': ['explorer.exe'], - '@timestamp': ['2024-05-07T12:48:45.004Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': ['C:\\Windows\\Explorer.EXE'], - 'host.risk.calculated_level': ['High'], - _id: ['6f8cd5e8021dbb64598f2b7ec56bee21fd00d1e62d4e08905f86bf234873ee66'], - 'process.hash.sha1': ['94518c310478e494082418ed295466f5aea26eea'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:37:18.152Z'], - }, - sort: [99, 1715086125004], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'ce110da958fe0cf0c07599a21c68d90a64c93b7607aa27970a614c7f49598316', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e', - ], - 'process.hash.md5': ['f070b5cf25febb9a88a168efd87c6112'], - 'event.category': ['malware', 'intrusion_detection', 'file'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [''], - 'process.parent.name': ['userinit.exe'], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '567be4d1e15f4ff96d92e7d28e191076f5813f50be96bf4c3916e4ecf53f66cd', - ], - 'process.pid': [6228], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['trusted'], - 'process.pe.original_file_name': ['EXPLORER.EXE'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Windows'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'file.hash.sha256': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\explorer.exe'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['Microsoft Windows'], - 'process.parent.executable': ['C:\\Windows\\System32\\userinit.exe'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e'], - 'process.args': ['C:\\Windows\\Explorer.EXE'], - 'process.code_signature.status': ['trusted'], - message: ['Malware Detection Alert'], - 'process.name': ['explorer.exe'], - '@timestamp': ['2024-05-07T12:48:45.001Z'], - 'process.parent.code_signature.trusted': [true], - 'process.command_line': ['C:\\Windows\\Explorer.EXE'], - 'host.risk.calculated_level': ['High'], - _id: ['ce110da958fe0cf0c07599a21c68d90a64c93b7607aa27970a614c7f49598316'], - 'process.hash.sha1': ['94518c310478e494082418ed295466f5aea26eea'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:36:43.813Z'], - }, - sort: [99, 1715086125001], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '0866787b0027b4d908767ac16e35a1da00970c83632ba85be65f2ad371132b4f', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['8cc83221870dd07144e63df594c391d9'], - 'event.category': ['malware', 'intrusion_detection', 'process', 'file'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'process.parent.name': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '33bc14d231a4afaa18f06513766d5f69d8b88f1e697cd127d24fb4b72ad44c7a', - ], - 'process.pid': [8708], - 'process.code_signature.exists': [true], - 'process.code_signature.subject_name': ['Microsoft Corporation'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Ransomware Detection Alert'], - 'host.name': ['SRVWIN02'], - 'Ransomware.files.data': [ - '2D002D002D003D003D003D0020005700', - '2D002D002D003D003D003D0020005700', - '2D002D002D003D003D003D0020005700', - ], - 'process.code_signature.trusted': [true], - 'Ransomware.files.metrics': ['CANARY_ACTIVITY'], - 'kibana.alert.workflow_status': ['open'], - 'process.parent.args_count': [1], - 'process.name': ['MsMpEng.exe'], - 'Ransomware.files.score': [0, 0, 0], - 'process.parent.code_signature.trusted': [false], - _id: ['0866787b0027b4d908767ac16e35a1da00970c83632ba85be65f2ad371132b4f'], - 'Ransomware.version': ['1.6.0'], - 'user.name': ['Administrator'], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['errorExpired'], - 'Ransomware.files.operation': ['creation', 'creation', 'creation'], - 'process.pe.original_file_name': ['MsMpEng.exe'], - 'event.module': ['endpoint'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\MsMpEng.exe'], - 'process.Ext.token.integrity_level_name': ['high'], - 'Ransomware.files.path': [ - 'c:\\hd3vuk19y-readme.txt', - 'c:\\$winreagent\\hd3vuk19y-readme.txt', - 'c:\\aaantiransomelastic-do-not-touch-dab6d40c-a6a1-442c-adc4-9d57a47e58d7\\hd3vuk19y-readme.txt', - ], - 'process.parent.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'process.parent.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'Ransomware.files.entropy': [3.629971457026797, 3.629971457026797, 3.629971457026797], - 'Ransomware.feature': ['canary'], - 'Ransomware.files.extension': ['txt', 'txt', 'txt'], - 'process.args': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.status': ['trusted'], - message: ['Ransomware Detection Alert'], - 'process.parent.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - '@timestamp': ['2024-05-07T12:48:45.000Z'], - 'process.command_line': ['"C:\\Windows\\MsMpEng.exe"'], - 'host.risk.calculated_level': ['High'], - 'process.hash.sha1': ['3d409b39b8502fcd23335a878f2cbdaf6d721995'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:22.964Z'], - }, - sort: [99, 1715086125000], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'b0fdf96721e361e1137d49a67e26d92f96b146392d7f44322bddc3d660abaef1', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['8cc83221870dd07144e63df594c391d9'], - 'event.category': ['malware', 'intrusion_detection'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'process.parent.name': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '33bc14d231a4afaa18f06513766d5f69d8b88f1e697cd127d24fb4b72ad44c7a', - ], - 'process.pid': [8708], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['errorExpired'], - 'process.pe.original_file_name': ['MsMpEng.exe'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Corporation'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Memory Threat Detection Alert: Shellcode Injection'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'process.parent.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'kibana.alert.workflow_status': ['open'], - 'process.args': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.status': ['trusted'], - message: ['Memory Threat Detection Alert: Shellcode Injection'], - 'process.parent.args_count': [1], - 'process.name': ['MsMpEng.exe'], - 'process.parent.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - '@timestamp': ['2024-05-07T12:48:44.996Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': ['"C:\\Windows\\MsMpEng.exe"'], - 'host.risk.calculated_level': ['High'], - _id: ['b0fdf96721e361e1137d49a67e26d92f96b146392d7f44322bddc3d660abaef1'], - 'process.hash.sha1': ['3d409b39b8502fcd23335a878f2cbdaf6d721995'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:22.174Z'], - }, - sort: [99, 1715086124996], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '7b4f49f21cf141e67856d3207fb4ea069c8035b41f0ea501970694cf8bd43cbe', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['8cc83221870dd07144e63df594c391d9'], - 'event.category': ['malware', 'intrusion_detection'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'process.parent.name': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '33bc14d231a4afaa18f06513766d5f69d8b88f1e697cd127d24fb4b72ad44c7a', - ], - 'process.pid': [8708], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['errorExpired'], - 'process.pe.original_file_name': ['MsMpEng.exe'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Corporation'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Memory Threat Detection Alert: Shellcode Injection'], - 'host.name': ['SRVWIN02'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'process.parent.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'kibana.alert.workflow_status': ['open'], - 'process.args': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.status': ['trusted'], - message: ['Memory Threat Detection Alert: Shellcode Injection'], - 'process.parent.args_count': [1], - 'process.name': ['MsMpEng.exe'], - 'process.parent.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - '@timestamp': ['2024-05-07T12:48:44.986Z'], - 'process.parent.code_signature.trusted': [false], - 'process.command_line': ['"C:\\Windows\\MsMpEng.exe"'], - 'host.risk.calculated_level': ['High'], - _id: ['7b4f49f21cf141e67856d3207fb4ea069c8035b41f0ea501970694cf8bd43cbe'], - 'process.hash.sha1': ['3d409b39b8502fcd23335a878f2cbdaf6d721995'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:22.066Z'], - }, - sort: [99, 1715086124986], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'ea81d79104cbd442236b5bcdb7a3331de897aa4ce1523e622068038d048d0a9e', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['8cc83221870dd07144e63df594c391d9'], - 'event.category': ['malware', 'intrusion_detection', 'process'], - 'host.risk.calculated_score_norm': [75.62723], - 'process.parent.command_line': [ - '"C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe" ', - ], - 'process.parent.name': [ - 'd55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '33bc14d231a4afaa18f06513766d5f69d8b88f1e697cd127d24fb4b72ad44c7a', - ], - 'process.Ext.memory_region.malware_signature.primary.matches': [ - 'WVmF9nQli1UIg2YEAIk+iwoLSgQ=', - 'dQxy0zPAQF9eW4vlXcMzwOv1VYvsgw==', - 'DIsEsIN4BAV1HP9wCP9wDP91DP8=', - '+4tF/FCLCP9RCF6Lx19bi+Vdw1U=', - 'vAAAADPSi030i/GLRfAPpMEBwe4f', - 'VIvO99GLwiNN3PfQM030I8czReiJ', - 'DIlGDIXAdSozwOtsi0YIhcB0Yms=', - ], - 'process.pid': [8708], - 'process.code_signature.exists': [true], - 'process.code_signature.subject_name': ['Microsoft Corporation'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': [ - 'Memory Threat Detection Alert: Windows.Ransomware.Sodinokibi', - ], - 'host.name': ['SRVWIN02'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'kibana.alert.workflow_status': ['open'], - 'rule.name': ['Windows.Ransomware.Sodinokibi'], - 'process.parent.args_count': [1], - 'process.Ext.memory_region.bytes_compressed_present': [false], - 'process.name': ['MsMpEng.exe'], - 'process.parent.code_signature.trusted': [false], - _id: ['ea81d79104cbd442236b5bcdb7a3331de897aa4ce1523e622068038d048d0a9e'], - 'user.name': ['Administrator'], - 'process.parent.code_signature.exists': [true], - 'process.parent.code_signature.status': ['errorExpired'], - 'process.pe.original_file_name': ['MsMpEng.exe'], - 'event.module': ['endpoint'], - 'process.Ext.memory_region.malware_signature.all_names': [ - 'Windows.Ransomware.Sodinokibi', - ], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\MsMpEng.exe'], - 'process.Ext.memory_region.malware_signature.primary.signature.name': [ - 'Windows.Ransomware.Sodinokibi', - ], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.code_signature.subject_name': ['PB03 TRANSPORT LTD.'], - 'process.parent.executable': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - 'process.args': ['C:\\Windows\\MsMpEng.exe'], - 'process.code_signature.status': ['trusted'], - message: ['Memory Threat Detection Alert: Windows.Ransomware.Sodinokibi'], - 'process.parent.args': [ - 'C:\\Users\\Administrator\\Desktop\\8813719803\\d55f983c994caa160ec63a59f6b4250fe67fb3e8c43a388aec60a4a6978e9f1e.exe', - ], - '@timestamp': ['2024-05-07T12:48:44.975Z'], - 'process.command_line': ['"C:\\Windows\\MsMpEng.exe"'], - 'host.risk.calculated_level': ['High'], - 'process.hash.sha1': ['3d409b39b8502fcd23335a878f2cbdaf6d721995'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-20T23:38:25.169Z'], - }, - sort: [99, 1715086124975], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: 'cdf3b5510bb5ed622e8cefd1ce6bedc52bdd99a4c1ead537af0603469e713c8b', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'file.path': ['C:\\Users\\Administrator\\AppData\\Local\\cdnver.dll'], - 'process.hash.md5': ['4bfef0b578515c16b9582e32b78d2594'], - 'event.category': ['malware', 'intrusion_detection', 'library'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': ['C:\\Programdata\\Q3C7N1V8.exe'], - 'process.parent.name': ['Q3C7N1V8.exe'], - 'user.name': ['Administrator'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '70d21cbdc527559c4931421e66aa819b86d5af5535445ace467e74518164c46a', - ], - 'process.pid': [7824], - 'process.code_signature.exists': [true], - 'process.parent.code_signature.exists': [false], - 'process.pe.original_file_name': ['RUNDLL32.EXE'], - 'event.module': ['endpoint'], - 'process.code_signature.subject_name': ['Microsoft Windows'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'file.hash.sha256': ['12e6642cf6413bdf5388bee663080fa299591b2ba023d069286f3be9647547c8'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': ['Malware Detection Alert'], - 'host.name': ['SRVWIN01'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\SysWOW64\\rundll32.exe'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.executable': ['C:\\ProgramData\\Q3C7N1V8.exe'], - 'kibana.alert.workflow_status': ['open'], - 'file.name': ['cdnver.dll'], - 'process.args': [ - 'C:\\Windows\\System32\\rundll32.exe', - 'C:\\Users\\Administrator\\AppData\\Local\\cdnver.dll,#1', - ], - 'process.code_signature.status': ['trusted'], - message: ['Malware Detection Alert'], - 'process.parent.args_count': [1], - 'process.name': ['rundll32.exe'], - 'process.parent.args': ['C:\\Programdata\\Q3C7N1V8.exe'], - '@timestamp': ['2024-05-07T12:47:32.838Z'], - 'process.command_line': [ - '"C:\\Windows\\System32\\rundll32.exe" "C:\\Users\\Administrator\\AppData\\Local\\cdnver.dll",#1', - ], - 'host.risk.calculated_level': ['High'], - _id: ['cdf3b5510bb5ed622e8cefd1ce6bedc52bdd99a4c1ead537af0603469e713c8b'], - 'process.hash.sha1': ['9b16507aaf10a0aafa0df2ba83e8eb2708d83a02'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-16T01:51:26.472Z'], - }, - sort: [99, 1715086052838], - }, - { - _index: '.internal.alerts-security.alerts-default-000001', - _id: '6abe81eb6350fb08031761be029e7ab19f7e577a7c17a9c5ea1ed010ba1620e3', - _score: null, - fields: { - 'kibana.alert.severity': ['critical'], - 'process.hash.md5': ['4bfef0b578515c16b9582e32b78d2594'], - 'event.category': ['malware', 'intrusion_detection'], - 'host.risk.calculated_score_norm': [73.02488], - 'process.parent.command_line': ['C:\\Programdata\\Q3C7N1V8.exe'], - 'process.parent.name': ['Q3C7N1V8.exe'], - 'user.risk.calculated_level': ['High'], - 'kibana.alert.rule.description': [ - 'Generates a detection alert each time an Elastic Endpoint Security alert is received. Enabling this rule allows you to immediately begin investigating your Endpoint alerts.', - ], - 'process.hash.sha256': [ - '70d21cbdc527559c4931421e66aa819b86d5af5535445ace467e74518164c46a', - ], - 'process.pid': [7824], - 'process.code_signature.exists': [true], - 'process.code_signature.subject_name': ['Microsoft Windows'], - 'host.os.version': ['21H2 (10.0.20348.1366)'], - 'kibana.alert.risk_score': [99], - 'user.risk.calculated_score_norm': [82.16188], - 'host.os.name': ['Windows'], - 'kibana.alert.rule.name': [ - 'Malicious Behavior Detection Alert: RunDLL32 with Unusual Arguments', - ], - 'host.name': ['SRVWIN01'], - 'event.outcome': ['success'], - 'process.code_signature.trusted': [true], - 'kibana.alert.workflow_status': ['open'], - 'rule.name': ['RunDLL32 with Unusual Arguments'], - 'threat.tactic.id': ['TA0005'], - 'threat.tactic.name': ['Defense Evasion'], - 'threat.technique.id': ['T1218'], - 'process.parent.args_count': [1], - 'threat.technique.subtechnique.reference': [ - 'https://attack.mitre.org/techniques/T1218/011/', - ], - 'process.name': ['rundll32.exe'], - 'threat.technique.subtechnique.name': ['Rundll32'], - _id: ['6abe81eb6350fb08031761be029e7ab19f7e577a7c17a9c5ea1ed010ba1620e3'], - 'threat.technique.name': ['System Binary Proxy Execution'], - 'threat.tactic.reference': ['https://attack.mitre.org/tactics/TA0005/'], - 'user.name': ['Administrator'], - 'threat.framework': ['MITRE ATT&CK'], - 'process.working_directory': ['C:\\Users\\Administrator\\Documents\\'], - 'process.pe.original_file_name': ['RUNDLL32.EXE'], - 'event.module': ['endpoint'], - 'user.domain': ['OMM-WIN-DETECT'], - 'process.executable': ['C:\\Windows\\SysWOW64\\rundll32.exe'], - 'process.Ext.token.integrity_level_name': ['high'], - 'process.parent.executable': ['C:\\ProgramData\\Q3C7N1V8.exe'], - 'process.args': [ - 'C:\\Windows\\System32\\rundll32.exe', - 'C:\\Users\\Administrator\\AppData\\Local\\cdnver.dll,#1', - ], - 'process.code_signature.status': ['trusted'], - message: ['Malicious Behavior Detection Alert: RunDLL32 with Unusual Arguments'], - 'process.parent.args': ['C:\\Programdata\\Q3C7N1V8.exe'], - '@timestamp': ['2024-05-07T12:47:32.836Z'], - 'threat.technique.subtechnique.id': ['T1218.011'], - 'threat.technique.reference': ['https://attack.mitre.org/techniques/T1218/'], - 'process.command_line': [ - '"C:\\Windows\\System32\\rundll32.exe" "C:\\Users\\Administrator\\AppData\\Local\\cdnver.dll",#1', - ], - 'host.risk.calculated_level': ['High'], - 'process.hash.sha1': ['9b16507aaf10a0aafa0df2ba83e8eb2708d83a02'], - 'event.dataset': ['endpoint.alerts'], - 'kibana.alert.original_time': ['2023-01-16T01:51:26.348Z'], - }, - sort: [99, 1715086052836], - }, - ], - }, -}; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts index 48e1619c2f00f..c94b14066947b 100644 --- a/x-pack/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts @@ -41,7 +41,7 @@ export const SECURITY_LABS_KNOWLEDGE_BASE_TOOL: AssistantTool = { `Key terms to retrieve Elastic Security Labs content for, like specific malware names or attack techniques.` ), }), - func: async (input, _, cbManager) => { + func: async (input) => { const docs = await kbDataClient.getKnowledgeBaseDocumentEntries({ kbResource: SECURITY_LABS_RESOURCE, query: input.question, diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 49566da1f6b18..cd6a13e30e014 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -11711,8 +11711,8 @@ "xpack.apm.serviceIcons.service": "Service", "xpack.apm.serviceIcons.serviceDetails.cloud.architecture": "Architecture", "xpack.apm.serviceIcons.serviceDetails.cloud.availabilityZoneLabel": "{zones, plural, =0 {Zone de disponibilité} one {Zone de disponibilité} other {Zones de disponibilité}}", - "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, =0 {Nom de fonction} one {Nom de fonction} other {Noms de fonction}}", "xpack.apm.serviceIcons.serviceDetails.cloud.faasTriggerTypeLabel": "{triggerTypes, plural, =0 {Type de déclencheur} one {Type de déclencheur} other {Types de déclencheurs}}", + "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, =0 {Nom de fonction} one {Nom de fonction} other {Noms de fonction}}", "xpack.apm.serviceIcons.serviceDetails.cloud.machineTypesLabel": "{machineTypes, plural, =0{Type de machine} one {Type de machine} other {Types de machines}}", "xpack.apm.serviceIcons.serviceDetails.cloud.projectIdLabel": "ID de projet", "xpack.apm.serviceIcons.serviceDetails.cloud.providerLabel": "Fournisseur cloud", @@ -16029,32 +16029,23 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.alertsLabel": "Alertes", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.alertsRangeSliderLabel": "Plage d'alertes", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.elserLabel": "ELSER configuré", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlDescription": "Documents de la base de connaissances pour générer des requêtes ES|QL", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlInstalledDescription": "Documents de la base de connaissances ES|QL chargés", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlLabel": "Documents de la base de connaissances ES|QL", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseDescription": "Index où sont stockés les documents de la base de connaissances", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "Initialisé sur `{kbIndexPattern}`", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseLabel": "Base de connaissances", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel": "Envoyez à l'Assistant d'IA des informations sur vos {alertsCount} alertes ouvertes ou confirmées les plus récentes et les plus risquées.", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.selectFewerAlertsLabel": "Envoyez moins d'alertes si la fenêtre contextuelle du modèle est trop petite.", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.sendAlertsLabel": "Envoyer des alertes", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsBadgeTitle": "Expérimental", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsDescription": "documentation", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsTitle": "Base de connaissances", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.setupKnowledgeBaseButton": "Configuration", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.setupKnowledgeBaseButtonToolTip": "Base de connaissances non disponible, veuillez consulter la documentation pour plus de détails.", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.yourAnonymizationSettingsLabel": "Vos paramètres d'anonymisation seront appliqués à ces alertes.", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnActionsLabel": "Actions", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnAuthorLabel": "Auteur", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnCreatedLabel": "Créé", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnEntriesLabel": "Entrées", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnNameLabel": "Nom", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSharingLabel": "Partage", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSpaceLabel": "Espace", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.createIndexTitle": "Nouvelle entrée d'index", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.defaultFlyoutTitle": "Base de connaissances", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryContent": "Vous ne pourrez pas récupérer cette entrée de la base de connaissances après sa suppression.", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryDefaultTitle": "Supprimer un élément", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryTitle": "Supprimer \"{title}\" ?", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.documentLabel": "Document", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.editDocumentEntryFlyoutTitle": "Modifier l'entrée du document", @@ -16065,7 +16056,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldInputLabel": "Champ", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldPlaceholder": "semantic_text", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryIndexNameInputLabel": "Index", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryInputPlaceholder": "Entrée", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryMarkdownInputText": "Texte de markdown", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputLabel": "Nom", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputPlaceholder": "Nommez votre entrée dans la base de connaissances", @@ -16083,7 +16073,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.knowledgeBasePrivate": "Privé", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.manualButtonLabel": "Manuel", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newDocumentEntryFlyoutTitle": "Nouvelle entrée de document", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newEntryTitle": "Nouvelle entrée", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newIndexEntryFlyoutTitle": "Nouvelle entrée d'index", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newLabel": "Nouveauté", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.searchPlaceholder": "Rechercher une entrée", @@ -16100,7 +16089,6 @@ "xpack.elasticAssistant.assistant.settings.settingsKnowledgeBaseMenuItemTitle": "Base de connaissances", "xpack.elasticAssistant.assistant.settings.settingsQuickPromptsMenuItemTitle": "Invites rapides", "xpack.elasticAssistant.assistant.settings.settingsSystemPromptsMenuItemTitle": "Invites système", - "xpack.elasticAssistant.assistant.settings.settingsTooltip": "Paramètres", "xpack.elasticAssistant.assistant.settings.settingsUpdatedToastTitle": "Paramètres mis à jour", "xpack.elasticAssistant.assistant.settings.showAnonymizedToggleLabel": "Afficher les anonymisés", "xpack.elasticAssistant.assistant.settings.showAnonymizedToggleRealValuesLabel": "Afficher les valeurs réelles", @@ -28300,8 +28288,8 @@ "xpack.maps.source.esSearch.descendingLabel": "décroissant", "xpack.maps.source.esSearch.extentFilterLabel": "Filtre dynamique pour les données de la zone de carte visible", "xpack.maps.source.esSearch.fieldNotFoundMsg": "Impossible de trouver \"{fieldName}\" dans le modèle d'indexation \"{indexPatternName}\".", - "xpack.maps.source.esSearch.geoFieldLabel": "Champ géospatial", "xpack.maps.source.esSearch.geofieldLabel": "Champ géospatial", + "xpack.maps.source.esSearch.geoFieldLabel": "Champ géospatial", "xpack.maps.source.esSearch.geoFieldTypeLabel": "Type de champ géospatial", "xpack.maps.source.esSearch.indexOverOneLengthEditError": "Votre vue de données pointe vers plusieurs index. Un seul index est autorisé par vue de données.", "xpack.maps.source.esSearch.indexZeroLengthEditError": "Votre vue de données ne pointe vers aucun index.", @@ -38077,8 +38065,8 @@ "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.maxAlertsFieldLessThanWarning": "Kibana ne permet qu'un maximum de {maxNumber} {maxNumber, plural, =1 {alerte} other {alertes}} par exécution de règle.", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.nameFieldRequiredError": "Nom obligatoire.", "xpack.securitySolution.detectionEngine.createRule.stepAboutrule.noteHelpText": "Ajouter un guide d'investigation sur les règles...", - "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText": "Fournissez des instructions sur les conditions préalables à la règle, telles que les intégrations requises, les étapes de configuration et tout ce qui est nécessaire au bon fonctionnement de la règle.", "xpack.securitySolution.detectionEngine.createRule.stepAboutrule.setupHelpText": "Ajouter le guide de configuration de règle...", + "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText": "Fournissez des instructions sur les conditions préalables à la règle, telles que les intégrations requises, les étapes de configuration et tout ce qui est nécessaire au bon fonctionnement de la règle.", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupLabel": "Guide de configuration", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.tagFieldEmptyError": "Une balise ne doit pas être vide", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.threatIndicatorPathFieldEmptyError": "Le remplacement du préfixe d'indicateur ne peut pas être vide.", @@ -43869,8 +43857,8 @@ "xpack.slo.sloEmbeddable.config.sloSelector.placeholder": "Sélectionner un SLO", "xpack.slo.sloEmbeddable.displayName": "Aperçu du SLO", "xpack.slo.sloEmbeddable.overview.sloNotFoundText": "Le SLO a été supprimé. Vous pouvez supprimer sans risque le widget du tableau de bord.", - "xpack.slo.sLOGridItem.targetFlexItemLabel": "Cible {target}", "xpack.slo.sloGridItem.targetFlexItemLabel": "Cible {target}", + "xpack.slo.sLOGridItem.targetFlexItemLabel": "Cible {target}", "xpack.slo.sloGroupConfiguration.customFiltersLabel": "Personnaliser le filtre", "xpack.slo.sloGroupConfiguration.customFiltersOptional": "Facultatif", "xpack.slo.sloGroupConfiguration.customFilterText": "Personnaliser le filtre", @@ -45400,8 +45388,8 @@ "xpack.stackConnectors.components.casesWebhookxpack.stackConnectors.components.casesWebhook.connectorTypeTitle": "Webhook - Données de gestion des cas", "xpack.stackConnectors.components.d3security.bodyCodeEditorAriaLabel": "Éditeur de code", "xpack.stackConnectors.components.d3security.bodyFieldLabel": "Corps", - "xpack.stackConnectors.components.d3Security.connectorTypeTitle": "D3 Security", "xpack.stackConnectors.components.d3security.connectorTypeTitle": "Données D3", + "xpack.stackConnectors.components.d3Security.connectorTypeTitle": "D3 Security", "xpack.stackConnectors.components.d3security.eventTypeFieldLabel": "Type d'événement", "xpack.stackConnectors.components.d3security.invalidActionText": "Nom d'action non valide.", "xpack.stackConnectors.components.d3security.requiredActionText": "L'action est requise.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f0cf7c38ac66b..0ed85fcd105e3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11694,8 +11694,8 @@ "xpack.apm.serviceIcons.service": "サービス", "xpack.apm.serviceIcons.serviceDetails.cloud.architecture": "アーキテクチャー", "xpack.apm.serviceIcons.serviceDetails.cloud.availabilityZoneLabel": "{zones, plural, other {可用性ゾーン}}", - "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, other {関数名}}", "xpack.apm.serviceIcons.serviceDetails.cloud.faasTriggerTypeLabel": "{triggerTypes, plural, other {トリガータイプ}}", + "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, other {関数名}}", "xpack.apm.serviceIcons.serviceDetails.cloud.machineTypesLabel": "{machineTypes, plural, other {コンピュータータイプ} }\n", "xpack.apm.serviceIcons.serviceDetails.cloud.projectIdLabel": "プロジェクト ID", "xpack.apm.serviceIcons.serviceDetails.cloud.providerLabel": "クラウドプロバイダー", @@ -16006,32 +16006,23 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.alertsLabel": "アラート", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.alertsRangeSliderLabel": "アラート範囲", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.elserLabel": "ELSERが構成されました", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlDescription": "ES|SQLクエリーを生成するためのナレッジベースドキュメント", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlInstalledDescription": "ES|QLナレッジベースドキュメントが読み込まれました", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlLabel": "ES|QLナレッジベースドキュメント", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseDescription": "ナレッジベースドキュメントが保存されているインデックス", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "`{kbIndexPattern}`に初期化されました", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseLabel": "ナレッジベース", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel": "{alertsCount}件の最新の最もリスクが高い未解決または確認済みのアラートに関する情報をAI Assistantに送信します。", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.selectFewerAlertsLabel": "モデルのコンテキストウィンドウが小さすぎるため、少ないアラートが送信されます。", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.sendAlertsLabel": "アラートを送信", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsBadgeTitle": "実験的", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsDescription": "ドキュメンテーション", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsTitle": "ナレッジベース", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.setupKnowledgeBaseButton": "セットアップ", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.setupKnowledgeBaseButtonToolTip": "ナレッジベースが利用できません。詳細については、ドキュメントを参照してください。", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.yourAnonymizationSettingsLabel": "匿名化設定がこれらのアラートに適用されます。", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnActionsLabel": "アクション", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnAuthorLabel": "作成者", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnCreatedLabel": "作成済み", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnEntriesLabel": "エントリ", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnNameLabel": "名前", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSharingLabel": "共有", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSpaceLabel": "スペース", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.createIndexTitle": "新しいインデックスエントリ", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.defaultFlyoutTitle": "ナレッジベース", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryContent": "このナレッジベースのエントリを削除すると、復元することはできません。", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryDefaultTitle": "アイテムを削除", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryTitle": "「{title}」を削除しますか?", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.documentLabel": "ドキュメント", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.editDocumentEntryFlyoutTitle": "ドキュメントエントリを編集", @@ -16042,7 +16033,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldInputLabel": "フィールド", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldPlaceholder": "semantic_text", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryIndexNameInputLabel": "インデックス", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryInputPlaceholder": "インプット", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryMarkdownInputText": "Markdownテキスト", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputLabel": "名前", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputPlaceholder": "ナレッジベースエントリの名前を指定", @@ -16060,7 +16050,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.knowledgeBasePrivate": "非公開", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.manualButtonLabel": "手動", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newDocumentEntryFlyoutTitle": "新しいドキュメントエントリ", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newEntryTitle": "新しいエントリー", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newIndexEntryFlyoutTitle": "新しいインデックスエントリ", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newLabel": "新規", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.searchPlaceholder": "エントリーを検索", @@ -16077,7 +16066,6 @@ "xpack.elasticAssistant.assistant.settings.settingsKnowledgeBaseMenuItemTitle": "ナレッジベース", "xpack.elasticAssistant.assistant.settings.settingsQuickPromptsMenuItemTitle": "クイックプロンプト", "xpack.elasticAssistant.assistant.settings.settingsSystemPromptsMenuItemTitle": "システムプロンプト", - "xpack.elasticAssistant.assistant.settings.settingsTooltip": "設定", "xpack.elasticAssistant.assistant.settings.settingsUpdatedToastTitle": "設定が更新されました", "xpack.elasticAssistant.assistant.settings.showAnonymizedToggleLabel": "匿名化して表示", "xpack.elasticAssistant.assistant.settings.showAnonymizedToggleRealValuesLabel": "実際の値を表示", @@ -28272,8 +28260,8 @@ "xpack.maps.source.esSearch.descendingLabel": "降順", "xpack.maps.source.esSearch.extentFilterLabel": "マップの表示範囲でデータを動的にフィルタリング", "xpack.maps.source.esSearch.fieldNotFoundMsg": "インデックスパターン''{indexPatternName}''に''{fieldName}''が見つかりません。", - "xpack.maps.source.esSearch.geoFieldLabel": "地理空間フィールド", "xpack.maps.source.esSearch.geofieldLabel": "地理空間フィールド", + "xpack.maps.source.esSearch.geoFieldLabel": "地理空間フィールド", "xpack.maps.source.esSearch.geoFieldTypeLabel": "地理空間フィールドタイプ", "xpack.maps.source.esSearch.indexOverOneLengthEditError": "データビューは複数のインデックスを参照しています。データビューごとに1つのインデックスのみが許可されています。", "xpack.maps.source.esSearch.indexZeroLengthEditError": "データビューはどのインデックスも参照していません。", @@ -38044,8 +38032,8 @@ "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.maxAlertsFieldLessThanWarning": "Kibanaで許可される最大数は、1回の実行につき、{maxNumber} {maxNumber, plural, other {アラート}}です。", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.nameFieldRequiredError": "名前が必要です。", "xpack.securitySolution.detectionEngine.createRule.stepAboutrule.noteHelpText": "ルール調査ガイドを追加...", - "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText": "必要な統合、構成ステップ、ルールが正常に動作するために必要な他のすべての項目といった、ルール前提条件に関する指示を入力します。", "xpack.securitySolution.detectionEngine.createRule.stepAboutrule.setupHelpText": "ルールセットアップガイドを追加...", + "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText": "必要な統合、構成ステップ、ルールが正常に動作するために必要な他のすべての項目といった、ルール前提条件に関する指示を入力します。", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupLabel": "セットアップガイド", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.tagFieldEmptyError": "タグを空にすることはできません", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.threatIndicatorPathFieldEmptyError": "インジケータープレフィックスの無効化を空にすることはできません", @@ -43833,8 +43821,8 @@ "xpack.slo.sloEmbeddable.config.sloSelector.placeholder": "SLOを選択", "xpack.slo.sloEmbeddable.displayName": "SLO概要", "xpack.slo.sloEmbeddable.overview.sloNotFoundText": "SLOが削除されました。ウィジェットをダッシュボードから安全に削除できます。", - "xpack.slo.sLOGridItem.targetFlexItemLabel": "目標{target}", "xpack.slo.sloGridItem.targetFlexItemLabel": "目標{target}", + "xpack.slo.sLOGridItem.targetFlexItemLabel": "目標{target}", "xpack.slo.sloGroupConfiguration.customFiltersLabel": "カスタムフィルター", "xpack.slo.sloGroupConfiguration.customFiltersOptional": "オプション", "xpack.slo.sloGroupConfiguration.customFilterText": "カスタムフィルター", @@ -45359,8 +45347,8 @@ "xpack.stackConnectors.components.casesWebhookxpack.stackConnectors.components.casesWebhook.connectorTypeTitle": "Webフック - ケース管理データ", "xpack.stackConnectors.components.d3security.bodyCodeEditorAriaLabel": "コードエディター", "xpack.stackConnectors.components.d3security.bodyFieldLabel": "本文", - "xpack.stackConnectors.components.d3Security.connectorTypeTitle": "D3セキュリティ", "xpack.stackConnectors.components.d3security.connectorTypeTitle": "D3データ", + "xpack.stackConnectors.components.d3Security.connectorTypeTitle": "D3セキュリティ", "xpack.stackConnectors.components.d3security.eventTypeFieldLabel": "イベントタイプ", "xpack.stackConnectors.components.d3security.invalidActionText": "無効なアクション名です。", "xpack.stackConnectors.components.d3security.requiredActionText": "アクションが必要です。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c69512018d0f4..b971c0ffca035 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11460,8 +11460,8 @@ "xpack.apm.serviceIcons.service": "服务", "xpack.apm.serviceIcons.serviceDetails.cloud.architecture": "架构", "xpack.apm.serviceIcons.serviceDetails.cloud.availabilityZoneLabel": "{zones, plural, other {可用性区域}}", - "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, other {功能名称}}", "xpack.apm.serviceIcons.serviceDetails.cloud.faasTriggerTypeLabel": "{triggerTypes, plural, other {触发类型}}", + "xpack.apm.serviceIcons.serviceDetails.cloud.functionNameLabel": "{functionNames, plural, other {功能名称}}", "xpack.apm.serviceIcons.serviceDetails.cloud.machineTypesLabel": "{machineTypes, plural, other {机器类型}}", "xpack.apm.serviceIcons.serviceDetails.cloud.projectIdLabel": "项目 ID", "xpack.apm.serviceIcons.serviceDetails.cloud.providerLabel": "云服务提供商", @@ -15689,32 +15689,23 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.alertsLabel": "告警", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.alertsRangeSliderLabel": "告警范围", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.elserLabel": "ELSER 已配置", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlDescription": "用于生成 ES|QL 查询的知识库文档", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlInstalledDescription": "已加载 ES|QL 知识库文档", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.esqlLabel": "ES|QL 知识库文档", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseDescription": "存储知识库文档的索引", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseInstalledDescription": "已初始化为 `{kbIndexPattern}`", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.knowledgeBaseLabel": "知识库", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel": "发送有关 {alertsCount} 个最新和风险最高的未决或已确认告警的 AI 助手信息。", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.selectFewerAlertsLabel": "如果此模型的上下文窗口太小,则发送更少告警。", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.sendAlertsLabel": "发送告警", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsBadgeTitle": "实验性", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsDescription": "文档", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.settingsTitle": "知识库", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.setupKnowledgeBaseButton": "设置", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.setupKnowledgeBaseButtonToolTip": "知识库不可用,请参阅文档了解详情。", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.yourAnonymizationSettingsLabel": "您的匿名处理设置将应用于这些告警。", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnActionsLabel": "操作", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnAuthorLabel": "作者", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnCreatedLabel": "创建时间", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnEntriesLabel": "条目", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnNameLabel": "名称", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSharingLabel": "共享", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.columnSpaceLabel": "工作区", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.createIndexTitle": "新索引条目", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.defaultFlyoutTitle": "知识库", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryContent": "此知识库条目一旦删除,将无法恢复。", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.deleteEntryDefaultTitle": "删除项", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.documentLabel": "文档", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.editDocumentEntryFlyoutTitle": "编辑文档条目", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.editIndexEntryFlyoutTitle": "编辑索引条目", @@ -15724,7 +15715,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldInputLabel": "字段", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryFieldPlaceholder": "semantic_text", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryIndexNameInputLabel": "索引", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryInputPlaceholder": "输入", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryMarkdownInputText": "Markdown 文本", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputLabel": "名称", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.entryNameInputPlaceholder": "为您的知识库条目命名", @@ -15742,7 +15732,6 @@ "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.knowledgeBasePrivate": "专用", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.manualButtonLabel": "手动", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newDocumentEntryFlyoutTitle": "新文档条目", - "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newEntryTitle": "新条目", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newIndexEntryFlyoutTitle": "新索引条目", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.newLabel": "新建", "xpack.elasticAssistant.assistant.settings.knowledgeBaseSettingsManagement.searchPlaceholder": "搜索条目", @@ -15759,7 +15748,6 @@ "xpack.elasticAssistant.assistant.settings.settingsKnowledgeBaseMenuItemTitle": "知识库", "xpack.elasticAssistant.assistant.settings.settingsQuickPromptsMenuItemTitle": "快速提示", "xpack.elasticAssistant.assistant.settings.settingsSystemPromptsMenuItemTitle": "系统提示", - "xpack.elasticAssistant.assistant.settings.settingsTooltip": "设置", "xpack.elasticAssistant.assistant.settings.settingsUpdatedToastTitle": "设置已更新", "xpack.elasticAssistant.assistant.settings.showAnonymizedToggleLabel": "显示已匿名处理项", "xpack.elasticAssistant.assistant.settings.showAnonymizedToggleRealValuesLabel": "显示实际值", @@ -27777,8 +27765,8 @@ "xpack.maps.source.esSearch.convertToGeoJsonErrorMsg": "无法将搜索响应转换成 geoJson 功能集合,错误:{errorMsg}", "xpack.maps.source.esSearch.descendingLabel": "降序", "xpack.maps.source.esSearch.extentFilterLabel": "在可见地图区域中动态筛留数据", - "xpack.maps.source.esSearch.geoFieldLabel": "地理空间字段", "xpack.maps.source.esSearch.geofieldLabel": "地理空间字段", + "xpack.maps.source.esSearch.geoFieldLabel": "地理空间字段", "xpack.maps.source.esSearch.geoFieldTypeLabel": "地理空间字段类型", "xpack.maps.source.esSearch.indexOverOneLengthEditError": "您的数据视图指向多个索引。每个数据视图只允许一个索引。", "xpack.maps.source.esSearch.indexZeroLengthEditError": "您的数据视图未指向任何索引。", @@ -37435,8 +37423,8 @@ "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.maxAlertsFieldLessThanWarning": "每次规则运行时,Kibana 最多只允许 {maxNumber} 个{maxNumber, plural, other {告警}}。", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.nameFieldRequiredError": "名称必填。", "xpack.securitySolution.detectionEngine.createRule.stepAboutrule.noteHelpText": "添加规则调查指南......", - "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText": "提供有关规则先决条件的说明,如所需集成、配置步骤,以及规则正常运行所需的任何其他内容。", "xpack.securitySolution.detectionEngine.createRule.stepAboutrule.setupHelpText": "添加规则设置指南......", + "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupHelpText": "提供有关规则先决条件的说明,如所需集成、配置步骤,以及规则正常运行所需的任何其他内容。", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.setupLabel": "设置指南", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.tagFieldEmptyError": "标签不得为空", "xpack.securitySolution.detectionEngine.createRule.stepAboutRule.threatIndicatorPathFieldEmptyError": "指标前缀覆盖不得为空", @@ -43176,8 +43164,8 @@ "xpack.slo.sloEmbeddable.config.sloSelector.placeholder": "选择 SLO", "xpack.slo.sloEmbeddable.displayName": "SLO 概览", "xpack.slo.sloEmbeddable.overview.sloNotFoundText": "SLO 已删除。您可以放心从仪表板中删除小组件。", - "xpack.slo.sLOGridItem.targetFlexItemLabel": "目标 {target}", "xpack.slo.sloGridItem.targetFlexItemLabel": "目标 {target}", + "xpack.slo.sLOGridItem.targetFlexItemLabel": "目标 {target}", "xpack.slo.sloGroupConfiguration.customFiltersLabel": "定制筛选", "xpack.slo.sloGroupConfiguration.customFiltersOptional": "可选", "xpack.slo.sloGroupConfiguration.customFilterText": "定制筛选", @@ -44654,8 +44642,8 @@ "xpack.stackConnectors.components.casesWebhookxpack.stackConnectors.components.casesWebhook.connectorTypeTitle": "Webhook - 案例管理数据", "xpack.stackConnectors.components.d3security.bodyCodeEditorAriaLabel": "代码编辑器", "xpack.stackConnectors.components.d3security.bodyFieldLabel": "正文", - "xpack.stackConnectors.components.d3Security.connectorTypeTitle": "D3 Security", "xpack.stackConnectors.components.d3security.connectorTypeTitle": "D3 数据", + "xpack.stackConnectors.components.d3Security.connectorTypeTitle": "D3 Security", "xpack.stackConnectors.components.d3security.eventTypeFieldLabel": "事件类型", "xpack.stackConnectors.components.d3security.invalidActionText": "操作名称无效。", "xpack.stackConnectors.components.d3security.requiredActionText": "'操作'必填。", From 9f545039ab42e95fd1d3d0518da4df6a8d040177 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 18 Nov 2024 11:32:53 -0700 Subject: [PATCH 19/20] fix dashboard grid item performs 2 DOM queries every render (#199390) Closes https://github.com/elastic/kibana/issues/199361 While investigating, I found that fetching DOM element with id `app-fixed-viewport` is a common pattern. I created the hook `useAppFixedViewport` to consolidate this logic into a single location. The hook only performs the DOM look-up on first render and then avoids the DOM look-up on each additional render. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine --- .github/CODEOWNERS | 1 + package.json | 1 + .../src/rendering_service.tsx | 3 ++- .../tsconfig.json | 3 ++- .../core-rendering-browser/README.md | 4 ++++ .../rendering/core-rendering-browser/index.ts | 10 +++++++++ .../core-rendering-browser/jest.config.js | 14 ++++++++++++ .../core-rendering-browser/kibana.jsonc | 5 +++++ .../core-rendering-browser/package.json | 7 ++++++ .../core-rendering-browser/src/index.ts | 10 +++++++++ .../src/use_app_fixed_viewport.ts | 17 ++++++++++++++ .../core-rendering-browser/tsconfig.json | 19 ++++++++++++++++ .../components/partition_vis_component.tsx | 3 ++- .../expression_partition_vis/tsconfig.json | 1 + .../public/components/xy_chart.tsx | 4 +++- .../expression_xy/tsconfig.json | 1 + .../filters_notification_popover.tsx | 5 ++--- .../component/grid/dashboard_grid.tsx | 22 +++++++++++++++++-- .../component/grid/dashboard_grid_item.tsx | 10 +++++---- .../component/viewport/dashboard_viewport.tsx | 6 +++-- .../embeddable/dashboard_container.tsx | 2 +- src/plugins/dashboard/tsconfig.json | 1 + tsconfig.base.json | 2 ++ .../waterfall/waterfall_bar_chart.tsx | 5 ++++- .../synthetics/tsconfig.json | 1 + .../components/waterfall_bar_chart.tsx | 5 ++++- .../uptime/tsconfig.json | 1 + yarn.lock | 4 ++++ 28 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 packages/core/rendering/core-rendering-browser/README.md create mode 100644 packages/core/rendering/core-rendering-browser/index.ts create mode 100644 packages/core/rendering/core-rendering-browser/jest.config.js create mode 100644 packages/core/rendering/core-rendering-browser/kibana.jsonc create mode 100644 packages/core/rendering/core-rendering-browser/package.json create mode 100644 packages/core/rendering/core-rendering-browser/src/index.ts create mode 100644 packages/core/rendering/core-rendering-browser/src/use_app_fixed_viewport.ts create mode 100644 packages/core/rendering/core-rendering-browser/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 760a4e0ba446e..a3c447a15b858 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -193,6 +193,7 @@ packages/core/plugins/core-plugins-server-mocks @elastic/kibana-core packages/core/preboot/core-preboot-server @elastic/kibana-core packages/core/preboot/core-preboot-server-internal @elastic/kibana-core packages/core/preboot/core-preboot-server-mocks @elastic/kibana-core +packages/core/rendering/core-rendering-browser @elastic/kibana-core packages/core/rendering/core-rendering-browser-internal @elastic/kibana-core packages/core/rendering/core-rendering-browser-mocks @elastic/kibana-core packages/core/rendering/core-rendering-server-internal @elastic/kibana-core diff --git a/package.json b/package.json index 1114f3a94ca6e..4c04880c56aa1 100644 --- a/package.json +++ b/package.json @@ -360,6 +360,7 @@ "@kbn/core-preboot-server": "link:packages/core/preboot/core-preboot-server", "@kbn/core-preboot-server-internal": "link:packages/core/preboot/core-preboot-server-internal", "@kbn/core-provider-plugin": "link:test/plugin_functional/plugins/core_provider_plugin", + "@kbn/core-rendering-browser": "link:packages/core/rendering/core-rendering-browser", "@kbn/core-rendering-browser-internal": "link:packages/core/rendering/core-rendering-browser-internal", "@kbn/core-rendering-server-internal": "link:packages/core/rendering/core-rendering-server-internal", "@kbn/core-root-browser-internal": "link:packages/core/root/core-root-browser-internal", diff --git a/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx index 700dad544cd2b..12a597ba9318f 100644 --- a/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx +++ b/packages/core/rendering/core-rendering-browser-internal/src/rendering_service.tsx @@ -18,6 +18,7 @@ import type { I18nStart } from '@kbn/core-i18n-browser'; import type { OverlayStart } from '@kbn/core-overlays-browser'; import type { ThemeServiceStart } from '@kbn/core-theme-browser'; import { KibanaRootContextProvider } from '@kbn/react-kibana-context-root'; +import { APP_FIXED_VIEWPORT_ID } from '@kbn/core-rendering-browser'; import { AppWrapper } from './app_containers'; interface StartServices { @@ -68,7 +69,7 @@ export class RenderingService { {/* The App Wrapper outside of the fixed headers that accepts custom class names from apps */} {/* Affixes a div to restrict the position of charts tooltip to the visible viewport minus the header */} -
    +
    {/* The actual plugin/app */} {appComponent} diff --git a/packages/core/rendering/core-rendering-browser-internal/tsconfig.json b/packages/core/rendering/core-rendering-browser-internal/tsconfig.json index 42c59f96b2471..4b0c009a0a033 100644 --- a/packages/core/rendering/core-rendering-browser-internal/tsconfig.json +++ b/packages/core/rendering/core-rendering-browser-internal/tsconfig.json @@ -26,7 +26,8 @@ "@kbn/core-analytics-browser-mocks", "@kbn/core-analytics-browser", "@kbn/core-i18n-browser", - "@kbn/core-theme-browser" + "@kbn/core-theme-browser", + "@kbn/core-rendering-browser" ], "exclude": [ "target/**/*", diff --git a/packages/core/rendering/core-rendering-browser/README.md b/packages/core/rendering/core-rendering-browser/README.md new file mode 100644 index 0000000000000..40141d7611e72 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/README.md @@ -0,0 +1,4 @@ +# @kbn/core-rendering-browser + +This package contains the types and implementation for Core's browser-side rendering service. + diff --git a/packages/core/rendering/core-rendering-browser/index.ts b/packages/core/rendering/core-rendering-browser/index.ts new file mode 100644 index 0000000000000..d8ccea264df05 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { APP_FIXED_VIEWPORT_ID, useAppFixedViewport } from './src'; diff --git a/packages/core/rendering/core-rendering-browser/jest.config.js b/packages/core/rendering/core-rendering-browser/jest.config.js new file mode 100644 index 0000000000000..13f1819553812 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/jest.config.js @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/core/rendering/core-rendering-browser'], +}; diff --git a/packages/core/rendering/core-rendering-browser/kibana.jsonc b/packages/core/rendering/core-rendering-browser/kibana.jsonc new file mode 100644 index 0000000000000..4b43c11865134 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-browser", + "id": "@kbn/core-rendering-browser", + "owner": "@elastic/kibana-core" +} diff --git a/packages/core/rendering/core-rendering-browser/package.json b/packages/core/rendering/core-rendering-browser/package.json new file mode 100644 index 0000000000000..4f1fa6f68ef01 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/core-rendering-browser", + "private": true, + "version": "1.0.0", + "author": "Kibana Core", + "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0" +} \ No newline at end of file diff --git a/packages/core/rendering/core-rendering-browser/src/index.ts b/packages/core/rendering/core-rendering-browser/src/index.ts new file mode 100644 index 0000000000000..aad756d296561 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/src/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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { APP_FIXED_VIEWPORT_ID, useAppFixedViewport } from './use_app_fixed_viewport'; diff --git a/packages/core/rendering/core-rendering-browser/src/use_app_fixed_viewport.ts b/packages/core/rendering/core-rendering-browser/src/use_app_fixed_viewport.ts new file mode 100644 index 0000000000000..ecf44a0018b49 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/src/use_app_fixed_viewport.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { useRef } from 'react'; + +export const APP_FIXED_VIEWPORT_ID = 'app-fixed-viewport'; + +export function useAppFixedViewport() { + const ref = useRef(document.getElementById(APP_FIXED_VIEWPORT_ID) ?? undefined); + return ref.current; +} diff --git a/packages/core/rendering/core-rendering-browser/tsconfig.json b/packages/core/rendering/core-rendering-browser/tsconfig.json new file mode 100644 index 0000000000000..3a932605dfa75 --- /dev/null +++ b/packages/core/rendering/core-rendering-browser/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "kbn_references": [], + "exclude": [ + "target/**/*", + ] +} diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx index 5baf582877a68..816a10509b425 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/partition_vis_component.tsx @@ -41,6 +41,7 @@ import { } from '@kbn/expressions-plugin/public'; import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import { getOverridesFor } from '@kbn/chart-expressions-common'; +import { useAppFixedViewport } from '@kbn/core-rendering-browser'; import { consolidateMetricColumns } from '../../common/utils'; import { DEFAULT_PERCENT_DECIMALS } from '../../common/constants'; import { @@ -385,7 +386,7 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { [visType, visParams, containerDimensions, rescaleFactor, hasOpenedOnAggBasedEditor] ); - const fixedViewPort = document.getElementById('app-fixed-viewport'); + const fixedViewPort = useAppFixedViewport(); const legendPosition = visParams.legendPosition ?? Position.Right; diff --git a/src/plugins/chart_expressions/expression_partition_vis/tsconfig.json b/src/plugins/chart_expressions/expression_partition_vis/tsconfig.json index 7669646f40a6b..1d8c4c4098728 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/tsconfig.json +++ b/src/plugins/chart_expressions/expression_partition_vis/tsconfig.json @@ -30,6 +30,7 @@ "@kbn/chart-expressions-common", "@kbn/cell-actions", "@kbn/react-kibana-context-render", + "@kbn/core-rendering-browser", ], "exclude": [ "target/**/*", diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index e1c428dd15c72..349af46eb101a 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -55,6 +55,7 @@ import { } from '@kbn/visualizations-plugin/common/constants'; import { PersistedState } from '@kbn/visualizations-plugin/public'; import { getOverridesFor, ChartSizeSpec } from '@kbn/chart-expressions-common'; +import { useAppFixedViewport } from '@kbn/core-rendering-browser'; import type { FilterEvent, BrushEvent, @@ -232,6 +233,7 @@ export function XYChart({ const chartRef = useRef(null); const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); + const appFixedViewport = useAppFixedViewport(); const filteredLayers = getFilteredLayers(layers); const layersById = filteredLayers.reduce>( (hashMap, layer) => ({ ...hashMap, [layer.layerId]: layer }), @@ -767,7 +769,7 @@ export function XYChart({ > , XYChartSeriesIdentifier> - boundary={document.getElementById('app-fixed-viewport') ?? undefined} + boundary={appFixedViewport} headerFormatter={ !args.detailedTooltip && xAxisColumn ? ({ value }) => ( diff --git a/src/plugins/chart_expressions/expression_xy/tsconfig.json b/src/plugins/chart_expressions/expression_xy/tsconfig.json index efa65a7f28a7d..cd8bd4db90b89 100644 --- a/src/plugins/chart_expressions/expression_xy/tsconfig.json +++ b/src/plugins/chart_expressions/expression_xy/tsconfig.json @@ -35,6 +35,7 @@ "@kbn/es-query", "@kbn/cell-actions", "@kbn/react-kibana-context-render", + "@kbn/core-rendering-browser", ], "exclude": [ "target/**/*", diff --git a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx index 5f23b21dc9155..5433646e3db8e 100644 --- a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx @@ -62,8 +62,7 @@ export function FiltersNotificationPopover({ api }: { api: FiltersNotificationAc } }, [api, setDisableEditButton]); - const [hasLockedHoverActions, dataViews, parentViewMode] = useBatchedOptionalPublishingSubjects( - api.hasLockedHoverActions$, + const [dataViews, parentViewMode] = useBatchedOptionalPublishingSubjects( api.parentApi?.dataViews, getViewModeSubject(api ?? undefined) ); @@ -77,7 +76,7 @@ export function FiltersNotificationPopover({ api }: { api: FiltersNotificationAc onClick={() => { setIsPopoverOpen(!isPopoverOpen); if (apiCanLockHoverActions(api)) { - api?.lockHoverActions(!hasLockedHoverActions); + api?.lockHoverActions(!api.hasLockedHoverActions$.value); } }} data-test-subj={`embeddablePanelNotification-${api.uuid}`} diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx index 0ef976af51eb6..76a545d1ea9fc 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx @@ -18,6 +18,7 @@ import { Layout, Responsive as ResponsiveReactGridLayout } from 'react-grid-layo import { ViewMode } from '@kbn/embeddable-plugin/public'; import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { useAppFixedViewport } from '@kbn/core-rendering-browser'; import { DashboardPanelState } from '../../../../common'; import { DashboardGridItem } from './dashboard_grid_item'; import { useDashboardGridSettings } from './use_dashboard_grid_settings'; @@ -25,7 +26,13 @@ import { useDashboardApi } from '../../../dashboard_api/use_dashboard_api'; import { getPanelLayoutsAreEqual } from '../../state/diffing/dashboard_diffing_utils'; import { DASHBOARD_GRID_HEIGHT, DASHBOARD_MARGIN_SIZE } from '../../../dashboard_constants'; -export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => { +export const DashboardGrid = ({ + dashboardContainer, + viewportWidth, +}: { + dashboardContainer?: HTMLElement; + viewportWidth: number; +}) => { const dashboardApi = useDashboardApi(); const [animatePanelTransforms, expandedPanelId, focusedPanelId, panels, useMargins, viewMode] = @@ -51,6 +58,8 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => { } }, [expandedPanelId]); + const appFixedViewport = useAppFixedViewport(); + const panelsInOrder: string[] = useMemo(() => { return Object.keys(panels).sort((embeddableIdA, embeddableIdB) => { const panelA = panels[embeddableIdA]; @@ -72,6 +81,8 @@ export const DashboardGrid = ({ viewportWidth }: { viewportWidth: number }) => { const type = panels[embeddableId].type; return ( { /> ); }); - }, [expandedPanelId, panels, panelsInOrder, focusedPanelId]); + }, [ + appFixedViewport, + dashboardContainer, + expandedPanelId, + panels, + panelsInOrder, + focusedPanelId, + ]); const onLayoutChange = useCallback( (newLayout: Array) => { diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx index 9b5a00c628608..5ad1363e6f8af 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid_item.tsx @@ -23,6 +23,8 @@ import { embeddableService, presentationUtilService } from '../../../services/ki type DivProps = Pick, 'className' | 'style' | 'children'>; export interface Props extends DivProps { + appFixedViewport?: HTMLElement; + dashboardContainer?: HTMLElement; id: DashboardPanelState['explicitInput']['id']; index?: number; type: DashboardPanelState['type']; @@ -35,6 +37,8 @@ export interface Props extends DivProps { export const Item = React.forwardRef( ( { + appFixedViewport, + dashboardContainer, expandedPanelId, focusedPanelId, id, @@ -92,10 +96,8 @@ export const Item = React.forwardRef( } }, [id, dashboardApi, scrollToPanelId, highlightPanelId, ref, blurPanel]); - const dashboardContainerTopOffset = - (document.querySelector('.dashboardContainer') as HTMLDivElement)?.offsetTop || 0; - const globalNavTopOffset = - (document.querySelector('#app-fixed-viewport') as HTMLDivElement)?.offsetTop || 0; + const dashboardContainerTopOffset = dashboardContainer?.offsetTop || 0; + const globalNavTopOffset = appFixedViewport?.offsetTop || 0; const focusStyles = blurPanel ? css` diff --git a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx index 027d2aee62b15..51f414bfcc298 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx @@ -41,7 +41,7 @@ export const useDebouncedWidthObserver = (skipDebounce = false, wait = 100) => { return { ref, width }; }; -export const DashboardViewport = () => { +export const DashboardViewport = ({ dashboardContainer }: { dashboardContainer?: HTMLElement }) => { const dashboardApi = useDashboardApi(); const [hasControls, setHasControls] = useState(false); const [ @@ -160,7 +160,9 @@ export const DashboardViewport = () => { otherwise, there is a race condition where the panels can end up being squashed TODO only render when dashboardInitialized */} - {viewportWidth !== 0 && } + {viewportWidth !== 0 && ( + + )}
    ); diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index e21a2f94bfc51..99f4fb7c2fa90 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -470,7 +470,7 @@ export class DashboardContainer coreStart={{ chrome: coreServices.chrome, customBranding: coreServices.customBranding }} > - + , diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index 3e95675ea64c3..1bf6827433b66 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -81,6 +81,7 @@ "@kbn/core-custom-branding-browser-mocks", "@kbn/core-mount-utils-browser", "@kbn/visualization-utils", + "@kbn/core-rendering-browser", ], "exclude": ["target/**/*"] } diff --git a/tsconfig.base.json b/tsconfig.base.json index f6aaa2ee0ac7f..b9270f4f035b8 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -542,6 +542,8 @@ "@kbn/core-preboot-server-mocks/*": ["packages/core/preboot/core-preboot-server-mocks/*"], "@kbn/core-provider-plugin": ["test/plugin_functional/plugins/core_provider_plugin"], "@kbn/core-provider-plugin/*": ["test/plugin_functional/plugins/core_provider_plugin/*"], + "@kbn/core-rendering-browser": ["packages/core/rendering/core-rendering-browser"], + "@kbn/core-rendering-browser/*": ["packages/core/rendering/core-rendering-browser/*"], "@kbn/core-rendering-browser-internal": ["packages/core/rendering/core-rendering-browser-internal"], "@kbn/core-rendering-browser-internal/*": ["packages/core/rendering/core-rendering-browser-internal/*"], "@kbn/core-rendering-browser-mocks": ["packages/core/rendering/core-rendering-browser-mocks"], diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_bar_chart.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_bar_chart.tsx index 3f0a80082aec6..cbe7507b3cfdc 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_bar_chart.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/step_details_page/step_waterfall_chart/waterfall/waterfall_bar_chart.tsx @@ -22,6 +22,7 @@ import { } from '@elastic/charts'; import { useEuiTheme } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useAppFixedViewport } from '@kbn/core-rendering-browser'; import { useBaseChartTheme } from '../../../../../../hooks/use_base_chart_theme'; import { BAR_HEIGHT } from './constants'; import { WaterfallChartChartContainer, WaterfallChartTooltip } from './styles'; @@ -86,6 +87,8 @@ export const WaterfallBarChart = ({ const handleProjectionClick = useMemo(() => onProjectionClick, [onProjectionClick]); const memoizedTickFormat = useCallback(tickFormat, [tickFormat]); + const appFixedViewport = useAppFixedViewport(); + return ( onProjectionClick, [onProjectionClick]); const memoizedTickFormat = useCallback(tickFormat, [tickFormat]); + const appFixedViewport = useAppFixedViewport(); + return ( Date: Mon, 18 Nov 2024 11:33:18 -0700 Subject: [PATCH 20/20] [uiActions] display toast when action.execute throws (#198571) Follow up to https://github.com/elastic/kibana/issues/198517. This PR updates Ui actions to be more robust and safely handle runtime errors. --------- Co-authored-by: Elastic Machine Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../public/actions/action_internal.test.ts | 38 +++++++++++++++++++ .../public/actions/action_internal.ts | 15 +++++++- src/plugins/ui_actions/public/plugin.ts | 3 +- src/plugins/ui_actions/public/services.ts | 4 +- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/plugins/ui_actions/public/actions/action_internal.test.ts b/src/plugins/ui_actions/public/actions/action_internal.test.ts index 5029811e80523..8bb0aadcc4677 100644 --- a/src/plugins/ui_actions/public/actions/action_internal.test.ts +++ b/src/plugins/ui_actions/public/actions/action_internal.test.ts @@ -20,4 +20,42 @@ describe('ActionInternal', () => { const action = new ActionInternal(defaultActionDef); expect(action.id).toBe('test-action'); }); + + describe('displays toasts when execute function throws', () => { + const addWarningMock = jest.fn(); + beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../services').getNotifications = () => ({ + toasts: { + addWarning: addWarningMock, + }, + }); + }); + + beforeEach(() => { + addWarningMock.mockReset(); + }); + + test('execute function is sync', async () => { + const action = new ActionInternal({ + id: 'test-action', + execute: () => { + throw new Error(''); + }, + }); + await action.execute({}); + expect(addWarningMock).toBeCalledTimes(1); + }); + + test('execute function is async', async () => { + const action = new ActionInternal({ + id: 'test-action', + execute: async () => { + throw new Error(''); + }, + }); + await action.execute({}); + expect(addWarningMock).toBeCalledTimes(1); + }); + }); }); diff --git a/src/plugins/ui_actions/public/actions/action_internal.ts b/src/plugins/ui_actions/public/actions/action_internal.ts index d9091551b87a1..ccef920ecc465 100644 --- a/src/plugins/ui_actions/public/actions/action_internal.ts +++ b/src/plugins/ui_actions/public/actions/action_internal.ts @@ -9,7 +9,9 @@ import * as React from 'react'; import type { Presentable, PresentableGrouping } from '@kbn/ui-actions-browser/src/types'; +import { i18n } from '@kbn/i18n'; import { Action, ActionDefinition, ActionMenuItemProps } from './action'; +import { getNotifications } from '../services'; /** * @internal @@ -45,8 +47,17 @@ export class ActionInternal } } - public execute(context: Context) { - return this.definition.execute(context); + public async execute(context: Context) { + try { + return await this.definition.execute(context); + } catch (e) { + getNotifications()?.toasts.addWarning( + i18n.translate('uiActions.execute.unhandledErrorMsg', { + defaultMessage: `Unable to execute action, error: {errorMessage}`, + values: { errorMessage: e.message }, + }) + ); + } } public getIconType(context: Context): string | undefined { diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 04461f15a6a69..988ef1116e715 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -16,7 +16,7 @@ import { addPanelMenuTrigger, } from '@kbn/ui-actions-browser/src/triggers'; import { UiActionsService } from './service'; -import { setAnalytics, setI18n, setTheme } from './services'; +import { setAnalytics, setI18n, setNotifications, setTheme } from './services'; export type UiActionsPublicSetup = Pick< UiActionsService, @@ -60,6 +60,7 @@ export class UiActionsPlugin public start(core: CoreStart): UiActionsPublicStart { setAnalytics(core.analytics); setI18n(core.i18n); + setNotifications(core.notifications); setTheme(core.theme); return this.service; } diff --git a/src/plugins/ui_actions/public/services.ts b/src/plugins/ui_actions/public/services.ts index abbfcc1feb944..ccb9520c3bcfb 100644 --- a/src/plugins/ui_actions/public/services.ts +++ b/src/plugins/ui_actions/public/services.ts @@ -7,9 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { AnalyticsServiceStart, I18nStart, ThemeServiceSetup } from '@kbn/core/public'; +import { AnalyticsServiceStart, CoreStart, I18nStart, ThemeServiceSetup } from '@kbn/core/public'; import { createGetterSetter } from '@kbn/kibana-utils-plugin/public'; export const [getAnalytics, setAnalytics] = createGetterSetter('Analytics'); export const [getI18n, setI18n] = createGetterSetter('I18n'); +export const [getNotifications, setNotifications] = + createGetterSetter('Notifications'); export const [getTheme, setTheme] = createGetterSetter('Theme');