diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index d7b1b6d02323a..1ee5e2e149a1f 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -35266,6 +35266,10 @@ paths: schema: nullable: true type: string + - in: query + name: associatedFilter + schema: + $ref: '#/components/schemas/Security_Timeline_API_AssociatedFilterType' responses: '200': content: @@ -49419,6 +49423,14 @@ components: Security_Osquery_API_VersionOrUndefined: $ref: '#/components/schemas/Security_Osquery_API_Version' nullable: true + Security_Timeline_API_AssociatedFilterType: + description: Filter notes based on their association with a document or saved object. + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + type: string Security_Timeline_API_BareNote: type: object properties: diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index d7b1b6d02323a..1ee5e2e149a1f 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -35266,6 +35266,10 @@ paths: schema: nullable: true type: string + - in: query + name: associatedFilter + schema: + $ref: '#/components/schemas/Security_Timeline_API_AssociatedFilterType' responses: '200': content: @@ -49419,6 +49423,14 @@ components: Security_Osquery_API_VersionOrUndefined: $ref: '#/components/schemas/Security_Osquery_API_Version' nullable: true + Security_Timeline_API_AssociatedFilterType: + description: Filter notes based on their association with a document or saved object. + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + type: string Security_Timeline_API_BareNote: type: object properties: diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index 24b0462ae93ef..8323fc524ebce 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -38697,6 +38697,10 @@ paths: schema: nullable: true type: string + - in: query + name: associatedFilter + schema: + $ref: '#/components/schemas/Security_Timeline_API_AssociatedFilterType' responses: '200': content: @@ -58185,6 +58189,14 @@ components: Security_Osquery_API_VersionOrUndefined: $ref: '#/components/schemas/Security_Osquery_API_Version' nullable: true + Security_Timeline_API_AssociatedFilterType: + description: Filter notes based on their association with a document or saved object. + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + type: string Security_Timeline_API_BareNote: type: object properties: diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index 24b0462ae93ef..8323fc524ebce 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -38697,6 +38697,10 @@ paths: schema: nullable: true type: string + - in: query + name: associatedFilter + schema: + $ref: '#/components/schemas/Security_Timeline_API_AssociatedFilterType' responses: '200': content: @@ -58185,6 +58189,14 @@ components: Security_Osquery_API_VersionOrUndefined: $ref: '#/components/schemas/Security_Osquery_API_Version' nullable: true + Security_Timeline_API_AssociatedFilterType: + description: Filter notes based on their association with a document or saved object. + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + type: string Security_Timeline_API_BareNote: type: object properties: diff --git a/packages/kbn-elastic-agent-utils/src/agent_names.ts b/packages/kbn-elastic-agent-utils/src/agent_names.ts index 0405da9cf2193..a78433780f1ad 100644 --- a/packages/kbn-elastic-agent-utils/src/agent_names.ts +++ b/packages/kbn-elastic-agent-utils/src/agent_names.ts @@ -38,37 +38,13 @@ export const ELASTIC_AGENT_NAMES: ElasticAgentName[] = [ ]; export type OpenTelemetryAgentName = - | 'otlp' | 'opentelemetry' - | 'opentelemetry/cpp' - | 'opentelemetry/dotnet' - | 'opentelemetry/erlang' - | 'opentelemetry/go' - | 'opentelemetry/java' - | 'opentelemetry/nodejs' - | 'opentelemetry/php' - | 'opentelemetry/python' - | 'opentelemetry/ruby' - | 'opentelemetry/rust' - | 'opentelemetry/swift' - | 'opentelemetry/android' - | 'opentelemetry/webjs' - | 'otlp/cpp' - | 'otlp/dotnet' - | 'otlp/erlang' - | 'otlp/go' - | 'otlp/java' - | 'otlp/nodejs' - | 'otlp/php' - | 'otlp/python' - | 'otlp/ruby' - | 'otlp/rust' - | 'otlp/swift' - | 'otlp/android' - | 'otlp/webjs'; + | 'otlp' + | `opentelemetry/${string}` + | `otlp/${string}`; +export const OPEN_TELEMETRY_BASE_AGENT_NAMES: OpenTelemetryAgentName[] = ['otlp', 'opentelemetry']; export const OPEN_TELEMETRY_AGENT_NAMES: OpenTelemetryAgentName[] = [ - 'otlp', - 'opentelemetry', + ...OPEN_TELEMETRY_BASE_AGENT_NAMES, 'opentelemetry/cpp', 'opentelemetry/dotnet', 'opentelemetry/erlang', diff --git a/packages/kbn-es-types/src/search.ts b/packages/kbn-es-types/src/search.ts index 3e67208bc2793..4c780fb2a2986 100644 --- a/packages/kbn-es-types/src/search.ts +++ b/packages/kbn-es-types/src/search.ts @@ -692,6 +692,7 @@ export interface ESQLSearchParams { query: string; filter?: unknown; locale?: string; + include_ccs_metadata?: boolean; dropNullColumns?: boolean; params?: Array>; } diff --git a/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts index e79c418eeb3cc..38e98104d41bd 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/columns.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('Column Identifier Expressions', () => { it('can parse un-quoted identifiers', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts index 30d44d447387e..6fb176c9624f7 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/commands.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('commands', () => { describe('correctly formatted, basic usage', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts index 101661973a692..f2f0fded57ca5 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/from.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('FROM', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts index 8ec533816a56e..9d822f78f9333 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/function.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { Walker } from '../../walker'; describe('function AST nodes', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts index d0650ab3f3213..889ca2a2ecf3d 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/inlinecast.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { ESQLFunction, ESQLInlineCast, ESQLSingleAstItem } from '../../types'; describe('Inline cast (::)', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts index 514d769d5c45e..7f50198c96047 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/literal.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { ESQLLiteral } from '../../types'; describe('literal expression', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts index 54ddc49c5d048..d33c94e8903ac 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/metrics.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('METRICS', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts index 8586236eeb2f9..e4b1a892d32d9 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/params.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; import { Walker } from '../../walker'; /** diff --git a/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts index 4acad891150b2..214e5c1d36882 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/rename.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('RENAME', () => { /** diff --git a/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts index cfaec0a6e39e9..981eac40b68ae 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/sort.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('SORT', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts b/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts index d507b559fd407..f3f6aeb886ec0 100644 --- a/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts +++ b/packages/kbn-esql-ast/src/parser/__tests__/where.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { getAstAndSyntaxErrors as parse } from '..'; +import { parse } from '..'; describe('WHERE', () => { describe('correctly formatted', () => { diff --git a/packages/kbn-esql-ast/src/parser/parser.ts b/packages/kbn-esql-ast/src/parser/parser.ts index ad263a49ebd00..f99e00e92d1e0 100644 --- a/packages/kbn-esql-ast/src/parser/parser.ts +++ b/packages/kbn-esql-ast/src/parser/parser.ts @@ -100,33 +100,66 @@ export interface ParseResult { } export const parse = (text: string | undefined, options: ParseOptions = {}): ParseResult => { - if (text == null) { - const commands: ESQLAstQueryExpression['commands'] = []; - return { ast: commands, root: Builder.expression.query(commands), errors: [], tokens: [] }; + try { + if (text == null) { + const commands: ESQLAstQueryExpression['commands'] = []; + return { ast: commands, root: Builder.expression.query(commands), errors: [], tokens: [] }; + } + const errorListener = new ESQLErrorListener(); + const parseListener = new ESQLAstBuilderListener(); + const { tokens, parser } = getParser( + CharStreams.fromString(text), + errorListener, + parseListener + ); + + parser[GRAMMAR_ROOT_RULE](); + + const errors = errorListener.getErrors().filter((error) => { + return !SYNTAX_ERRORS_TO_IGNORE.includes(error.message); + }); + const { ast: commands } = parseListener.getAst(); + const root = Builder.expression.query(commands, { + location: { + min: 0, + max: text.length - 1, + }, + }); + + if (options.withFormatting) { + const decorations = collectDecorations(tokens); + attachDecorations(root, tokens.tokens, decorations.lines); + } + + return { root, ast: commands, errors, tokens: tokens.tokens }; + } catch (error) { + /** + * Parsing should never fail, meaning this branch should never execute. But + * if it does fail, we want to log the error message for easier debugging. + */ + // eslint-disable-next-line no-console + console.error(error); + + const root = Builder.expression.query(); + + return { + root, + ast: root.commands, + errors: [ + { + startLineNumber: 0, + endLineNumber: 0, + startColumn: 0, + endColumn: 0, + message: + 'Parsing internal error: ' + + (!!error && typeof error === 'object' ? String(error.message) : String(error)), + severity: 'error', + }, + ], + tokens: [], + }; } - const errorListener = new ESQLErrorListener(); - const parseListener = new ESQLAstBuilderListener(); - const { tokens, parser } = getParser(CharStreams.fromString(text), errorListener, parseListener); - - parser[GRAMMAR_ROOT_RULE](); - - const errors = errorListener.getErrors().filter((error) => { - return !SYNTAX_ERRORS_TO_IGNORE.includes(error.message); - }); - const { ast: commands } = parseListener.getAst(); - const root = Builder.expression.query(commands, { - location: { - min: 0, - max: text.length - 1, - }, - }); - - if (options.withFormatting) { - const decorations = collectDecorations(tokens); - attachDecorations(root, tokens.tokens, decorations.lines); - } - - return { root, ast: commands, errors, tokens: tokens.tokens }; }; export const parseErrors = (text: string) => { diff --git a/packages/kbn-telemetry-tools/src/tools/__fixture__/telemetry_collectors/constants.ts b/packages/kbn-telemetry-tools/src/tools/__fixture__/telemetry_collectors/constants.ts index c9b0cbfe0281a..ec8b935912cd7 100644 --- a/packages/kbn-telemetry-tools/src/tools/__fixture__/telemetry_collectors/constants.ts +++ b/packages/kbn-telemetry-tools/src/tools/__fixture__/telemetry_collectors/constants.ts @@ -60,6 +60,9 @@ export interface MappedTypes { mappedTypeWithOneInlineProp: { [key in 'prop3']: number; }; + mappedTypeWithLiteralTemplates: { + [key in MappedTypeProps | `templated_prop/${string}`]: number; + }; } export type RecordWithKnownProps = Record; diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts index 4301ab30fd09e..86f897767a025 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts @@ -105,6 +105,13 @@ describe('getDescriptor', () => { mappedTypeWithOneInlineProp: { prop3: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, }, + mappedTypeWithLiteralTemplates: { + prop1: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + prop2: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + // ideally, it'd be `templated_prop/@@INDEX@@` to be more explicit. But we're going with the fuzzier approach + // for now as it may require more changes downstream that are not worth it. + '@@INDEX@@': { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + }, }); }); diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index 2523bc246d2a8..0269dc446b4a0 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -99,10 +99,15 @@ export function getConstraints(node: ts.Node, program: ts.Program): any { return node.literal.text; } - if (ts.isStringLiteral(node)) { + if (ts.isStringLiteral(node) || ts.isStringLiteralLike(node)) { return node.text; } + // template literals such as `smth/${string}` + if (ts.isTemplateLiteralTypeNode(node) || ts.isTemplateExpression(node)) { + return '@@INDEX@@'; // just map it to any kind of string. We can enforce it further in the future if we see fit. + } + if (ts.isImportSpecifier(node) || ts.isExportSpecifier(node)) { const source = node.getSourceFile(); const importedModuleName = getModuleSpecifier(node); @@ -180,9 +185,9 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | const constraintsArray = Array.isArray(constraints) ? constraints : [constraints]; if (typeof constraintsArray[0] === 'string') { return constraintsArray.reduce((acc, c) => { - (acc as Record)[c] = descriptor; + acc[c] = descriptor; return acc; - }, {}); + }, {} as Record); } } return { '@@INDEX@@': descriptor }; diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index 966500710fd45..a93996f163962 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -164,6 +164,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { query, // time_zone: timezone, locale, + include_ccs_metadata: true, }; if (input) { const esQueryConfigs = getEsQueryConfig( diff --git a/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts b/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts index 0757304199d8a..6a7a3fc80343e 100644 --- a/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts +++ b/test/functional/apps/discover/group2_data_grid1/_data_grid_context.ts @@ -40,7 +40,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const browser = getService('browser'); const security = getService('security'); - describe('discover data grid context tests', () => { + // Failing: See https://github.com/elastic/kibana/issues/196120 + describe.skip('discover data grid context tests', () => { before(async () => { await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); diff --git a/x-pack/packages/security/plugin_types_public/index.ts b/x-pack/packages/security/plugin_types_public/index.ts index a48511441382a..fc8829ad8a5f8 100644 --- a/x-pack/packages/security/plugin_types_public/index.ts +++ b/x-pack/packages/security/plugin_types_public/index.ts @@ -24,3 +24,4 @@ export type { } from './src/roles'; export { PrivilegesAPIClientPublicContract } from './src/privileges'; export type { PrivilegesAPIClientGetAllArgs } from './src/privileges'; +export type { SecurityLicense } from './src/license'; diff --git a/x-pack/packages/security/plugin_types_public/src/license/index.ts b/x-pack/packages/security/plugin_types_public/src/license/index.ts new file mode 100644 index 0000000000000..0c1ec0431c10a --- /dev/null +++ b/x-pack/packages/security/plugin_types_public/src/license/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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SecurityPluginSetup } from '../plugin'; + +export type SecurityLicense = SecurityPluginSetup['license']; diff --git a/x-pack/plugins/cases/public/components/use_breadcrumbs/index.test.tsx b/x-pack/plugins/cases/public/components/use_breadcrumbs/index.test.tsx index d34e3c112d9fb..9f48783fde24d 100644 --- a/x-pack/plugins/cases/public/components/use_breadcrumbs/index.test.tsx +++ b/x-pack/plugins/cases/public/components/use_breadcrumbs/index.test.tsx @@ -54,10 +54,10 @@ describe('useCasesBreadcrumbs', () => { describe('set all_cases breadcrumbs', () => { it('call setBreadcrumbs with all items', async () => { renderHook(() => useCasesBreadcrumbs(CasesDeepLinkId.cases), { wrapper }); - expect(mockSetBreadcrumbs).toHaveBeenCalledWith([ - { href: '/test', onClick: expect.any(Function), text: 'Test' }, - { text: 'Cases' }, - ]); + expect(mockSetBreadcrumbs).toHaveBeenCalledWith( + [{ href: '/test', onClick: expect.any(Function), text: 'Test' }, { text: 'Cases' }], + { project: { value: [] } } + ); expect(mockSetServerlessBreadcrumbs).toHaveBeenCalledWith([]); }); @@ -76,11 +76,14 @@ describe('useCasesBreadcrumbs', () => { describe('set create_case breadcrumbs', () => { it('call setBreadcrumbs with all items', () => { renderHook(() => useCasesBreadcrumbs(CasesDeepLinkId.casesCreate), { wrapper }); - expect(mockSetBreadcrumbs).toHaveBeenCalledWith([ - { href: '/test', onClick: expect.any(Function), text: 'Test' }, - { href: CasesDeepLinkId.cases, onClick: expect.any(Function), text: 'Cases' }, - { text: 'Create' }, - ]); + expect(mockSetBreadcrumbs).toHaveBeenCalledWith( + [ + { href: '/test', onClick: expect.any(Function), text: 'Test' }, + { href: CasesDeepLinkId.cases, onClick: expect.any(Function), text: 'Cases' }, + { text: 'Create' }, + ], + { project: { value: [] } } + ); expect(mockSetServerlessBreadcrumbs).toHaveBeenCalledWith([]); }); @@ -100,11 +103,14 @@ describe('useCasesBreadcrumbs', () => { const title = 'Fake Title'; it('call setBreadcrumbs with title', () => { renderHook(() => useCasesTitleBreadcrumbs(title), { wrapper }); - expect(mockSetBreadcrumbs).toHaveBeenCalledWith([ - { href: '/test', onClick: expect.any(Function), text: 'Test' }, - { href: CasesDeepLinkId.cases, onClick: expect.any(Function), text: 'Cases' }, - { text: title }, - ]); + expect(mockSetBreadcrumbs).toHaveBeenCalledWith( + [ + { href: '/test', onClick: expect.any(Function), text: 'Test' }, + { href: CasesDeepLinkId.cases, onClick: expect.any(Function), text: 'Cases' }, + { text: title }, + ], + { project: { value: [{ text: title }] } } + ); expect(mockSetServerlessBreadcrumbs).toHaveBeenCalledWith([{ text: title }]); }); @@ -123,11 +129,14 @@ describe('useCasesBreadcrumbs', () => { describe('set settings breadcrumbs', () => { it('call setBreadcrumbs with all items', () => { renderHook(() => useCasesBreadcrumbs(CasesDeepLinkId.casesConfigure), { wrapper }); - expect(mockSetBreadcrumbs).toHaveBeenCalledWith([ - { href: '/test', onClick: expect.any(Function), text: 'Test' }, - { href: CasesDeepLinkId.cases, onClick: expect.any(Function), text: 'Cases' }, - { text: 'Settings' }, - ]); + expect(mockSetBreadcrumbs).toHaveBeenCalledWith( + [ + { href: '/test', onClick: expect.any(Function), text: 'Test' }, + { href: CasesDeepLinkId.cases, onClick: expect.any(Function), text: 'Cases' }, + { text: 'Settings' }, + ], + { project: { value: [] } } + ); expect(mockSetServerlessBreadcrumbs).toHaveBeenCalledWith([]); }); diff --git a/x-pack/plugins/cases/public/components/use_breadcrumbs/index.ts b/x-pack/plugins/cases/public/components/use_breadcrumbs/index.ts index 6312918842ba3..1750cd54e7d53 100644 --- a/x-pack/plugins/cases/public/components/use_breadcrumbs/index.ts +++ b/x-pack/plugins/cases/public/components/use_breadcrumbs/index.ts @@ -29,34 +29,48 @@ function getTitleFromBreadcrumbs(breadcrumbs: ChromeBreadcrumb[]): string[] { return breadcrumbs.map(({ text }) => text?.toString() ?? '').reverse(); } +const useGetBreadcrumbsWithNavigation = () => { + const { navigateToUrl } = useKibana().services.application; + + return useCallback( + (breadcrumbs: ChromeBreadcrumb[]): ChromeBreadcrumb[] => { + return breadcrumbs.map((breadcrumb) => { + const { href, onClick } = breadcrumb; + if (!href || onClick) { + return breadcrumb; + } + return { + ...breadcrumb, + onClick: (event) => { + if (event) { + event.preventDefault(); + } + navigateToUrl(href); + }, + }; + }); + }, + [navigateToUrl] + ); +}; + const useApplyBreadcrumbs = () => { - const { - chrome: { docTitle, setBreadcrumbs }, - application: { navigateToUrl }, - } = useKibana().services; + const { docTitle, setBreadcrumbs } = useKibana().services.chrome; + const getBreadcrumbsWithNavigation = useGetBreadcrumbsWithNavigation(); + return useCallback( - (breadcrumbs: ChromeBreadcrumb[]) => { - docTitle.change(getTitleFromBreadcrumbs(breadcrumbs)); - setBreadcrumbs( - breadcrumbs.map((breadcrumb) => { - const { href, onClick } = breadcrumb; - return { - ...breadcrumb, - ...(href && !onClick - ? { - onClick: (event) => { - if (event) { - event.preventDefault(); - } - navigateToUrl(href); - }, - } - : {}), - }; - }) - ); + ( + leadingRawBreadcrumbs: ChromeBreadcrumb[], + trailingRawBreadcrumbs: ChromeBreadcrumb[] = [] + ) => { + const leadingBreadcrumbs = getBreadcrumbsWithNavigation(leadingRawBreadcrumbs); + const trailingBreadcrumbs = getBreadcrumbsWithNavigation(trailingRawBreadcrumbs); + const allBreadcrumbs = [...leadingBreadcrumbs, ...trailingBreadcrumbs]; + + docTitle.change(getTitleFromBreadcrumbs(allBreadcrumbs)); + setBreadcrumbs(allBreadcrumbs, { project: { value: trailingBreadcrumbs } }); }, - [docTitle, setBreadcrumbs, navigateToUrl] + [docTitle, setBreadcrumbs, getBreadcrumbsWithNavigation] ); }; @@ -103,9 +117,8 @@ export const useCasesTitleBreadcrumbs = (caseTitle: string) => { text: casesBreadcrumbTitle[CasesDeepLinkId.cases], href: getAppUrl({ deepLinkId: CasesDeepLinkId.cases }), }, - titleBreadcrumb, ]; - applyBreadcrumbs(casesBreadcrumbs); + applyBreadcrumbs(casesBreadcrumbs, [titleBreadcrumb]); KibanaServices.get().serverless?.setBreadcrumbs([titleBreadcrumb]); }, [caseTitle, appTitle, getAppUrl, applyBreadcrumbs]); }; diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index b1473c56b37ed..efc564089fb43 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -173,7 +173,7 @@ export const SearchBar: FC = (opts) => { reportEvent.searchRequest(); } - const rawParams = parseSearchParams(searchValue.toLowerCase()); + const rawParams = parseSearchParams(searchValue.toLowerCase(), searchableTypes); let tagIds: string[] | undefined; if (taggingApi && rawParams.filters.tags) { tagIds = rawParams.filters.tags.map( diff --git a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts index c6df745be847f..8e24f599ce1d2 100644 --- a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts +++ b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.test.ts @@ -9,12 +9,12 @@ import { parseSearchParams } from './parse_search_params'; describe('parseSearchParams', () => { it('returns the correct term', () => { - const searchParams = parseSearchParams('tag:(my-tag OR other-tag) hello'); + const searchParams = parseSearchParams('tag:(my-tag OR other-tag) hello', []); expect(searchParams.term).toEqual('hello'); }); it('returns the raw query as `term` in case of parsing error', () => { - const searchParams = parseSearchParams('tag:((()^invalid'); + const searchParams = parseSearchParams('tag:((()^invalid', []); expect(searchParams).toEqual({ term: 'tag:((()^invalid', filters: {}, @@ -22,12 +22,12 @@ describe('parseSearchParams', () => { }); it('returns `undefined` term if query only contains field clauses', () => { - const searchParams = parseSearchParams('tag:(my-tag OR other-tag)'); + const searchParams = parseSearchParams('tag:(my-tag OR other-tag)', []); expect(searchParams.term).toBeUndefined(); }); it('returns correct filters when no field clause is defined', () => { - const searchParams = parseSearchParams('hello'); + const searchParams = parseSearchParams('hello', []); expect(searchParams.filters).toEqual({ tags: undefined, types: undefined, @@ -35,7 +35,7 @@ describe('parseSearchParams', () => { }); it('returns correct filters when field clauses are present', () => { - const searchParams = parseSearchParams('tag:foo type:bar hello tag:dolly'); + const searchParams = parseSearchParams('tag:foo type:bar hello tag:dolly', []); expect(searchParams).toEqual({ term: 'hello', filters: { @@ -46,7 +46,7 @@ describe('parseSearchParams', () => { }); it('considers unknown field clauses to be part of the raw search term', () => { - const searchParams = parseSearchParams('tag:foo unknown:bar hello'); + const searchParams = parseSearchParams('tag:foo unknown:bar hello', []); expect(searchParams).toEqual({ term: 'unknown:bar hello', filters: { @@ -56,7 +56,7 @@ describe('parseSearchParams', () => { }); it('handles aliases field clauses', () => { - const searchParams = parseSearchParams('tag:foo tags:bar type:dash types:board hello'); + const searchParams = parseSearchParams('tag:foo tags:bar type:dash types:board hello', []); expect(searchParams).toEqual({ term: 'hello', filters: { @@ -67,7 +67,7 @@ describe('parseSearchParams', () => { }); it('converts boolean and number values to string for known filters', () => { - const searchParams = parseSearchParams('tag:42 tags:true type:69 types:false hello'); + const searchParams = parseSearchParams('tag:42 tags:true type:69 types:false hello', []); expect(searchParams).toEqual({ term: 'hello', filters: { @@ -76,4 +76,74 @@ describe('parseSearchParams', () => { }, }); }); + + it('converts multiword searchable types to phrases so they get picked up as types', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad', 'enterprise search']; + const searchParams = parseSearchParams( + 'type:canvas workpad types:canvas-workpad hello type:enterprise search type:not multiword', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello multiword', + filters: { + types: ['canvas workpad', 'enterprise search', 'not'], + }, + }); + }); + + it('parses correctly when multiword types are already quoted', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad']; + const searchParams = parseSearchParams( + `type:"canvas workpad" hello type:"dashboard"`, + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['canvas workpad', 'dashboard'], + }, + }); + }); + + it('parses correctly when there is whitespace between type keyword and value', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad']; + const searchParams = parseSearchParams( + 'type: canvas workpad hello type: dashboard', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['canvas workpad', 'dashboard'], + }, + }); + }); + + it('dedupes duplicate types', () => { + const mockSearchableMultiwordTypes = ['canvas-workpad']; + const searchParams = parseSearchParams( + 'type:canvas workpad hello type:dashboard type:canvas-workpad type:canvas workpad type:dashboard', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['canvas workpad', 'dashboard'], + }, + }); + }); + + it('handles whitespace removal even if there are no multiword types', () => { + const mockSearchableMultiwordTypes: string[] = []; + const searchParams = parseSearchParams( + 'hello type: dashboard', + mockSearchableMultiwordTypes + ); + expect(searchParams).toEqual({ + term: 'hello', + filters: { + types: ['dashboard'], + }, + }); + }); }); diff --git a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts index 1df6c1123a328..90ba36cce5fcb 100644 --- a/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts +++ b/x-pack/plugins/global_search_bar/public/search_syntax/parse_search_params.ts @@ -16,12 +16,54 @@ const aliasMap = { type: ['types'], }; -export const parseSearchParams = (term: string): ParsedSearchParams => { +// Converts multiword types to phrases by wrapping them in quotes and trimming whitespace after type keyword. Example: type: canvas workpad -> type:"canvas workpad". If the type is already wrapped in quotes or is a single word, it will only trim whitespace after type keyword. +const convertMultiwordTypesToPhrasesAndTrimWhitespace = ( + term: string, + multiWordTypes: string[] +): string => { + if (!multiWordTypes.length) { + return term.replace( + /(type:|types:)\s*([^"']*?)\b([^"'\s]+)/gi, + (_, typeKeyword, whitespace, typeValue) => `${typeKeyword}${whitespace.trim()}${typeValue}` + ); + } + + const typesPattern = multiWordTypes.join('|'); + const termReplaceRegex = new RegExp( + `(type:|types:)\\s*([^"']*?)\\b((${typesPattern})\\b|[^\\s"']+)`, + 'gi' + ); + + return term.replace(termReplaceRegex, (_, typeKeyword, whitespace, typeValue) => { + const trimmedTypeKeyword = `${typeKeyword}${whitespace.trim()}`; + + // If the type value is already wrapped in quotes, leave it as is + return /['"]/.test(typeValue) + ? `${trimmedTypeKeyword}${typeValue}` + : `${trimmedTypeKeyword}"${typeValue}"`; + }); +}; + +const dedupeTypes = (types: FilterValues): FilterValues => [ + ...new Set(types.map((item) => item.replace(/[-\s]+/g, ' ').trim())), +]; + +export const parseSearchParams = (term: string, searchableTypes: string[]): ParsedSearchParams => { const recognizedFields = knownFilters.concat(...Object.values(aliasMap)); let query: Query; + // Finds all multiword types that are separated by whitespace or hyphens + const multiWordSearchableTypesWhitespaceSeperated = searchableTypes + .filter((item) => /[ -]/.test(item)) + .map((item) => item.replace(/-/g, ' ')); + + const modifiedTerm = convertMultiwordTypesToPhrasesAndTrimWhitespace( + term, + multiWordSearchableTypesWhitespaceSeperated + ); + try { - query = Query.parse(term, { + query = Query.parse(modifiedTerm, { schema: { recognizedFields }, }); } catch (e) { @@ -42,7 +84,7 @@ export const parseSearchParams = (term: string): ParsedSearchParams => { term: searchTerm, filters: { tags: tags ? valuesToString(tags) : undefined, - types: types ? valuesToString(types) : undefined, + types: types ? dedupeTypes(valuesToString(types)) : undefined, }, }; }; diff --git a/x-pack/plugins/observability_solution/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/observability_solution/apm/common/__snapshots__/apm_telemetry.test.ts.snap index d0aa75c2170f1..e719db52a397b 100644 --- a/x-pack/plugins/observability_solution/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/observability_solution/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -1246,6 +1246,2616 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } } + }, + "otlp": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/cpp": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/dotnet": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/erlang": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/go": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/java": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/nodejs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/php": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/python": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/ruby": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/rust": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/swift": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/android": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "opentelemetry/webjs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/cpp": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/dotnet": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/erlang": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/go": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/java": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/nodejs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/php": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/python": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/ruby": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/rust": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/swift": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/android": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "otlp/webjs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } + }, + "ios/swift": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent versions within the last day" + } + }, + "activation_method": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 agent activation methods within the last day" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service framework version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service framework and version sorted by doc count" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service language version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service language name and version sorted by doc count." + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime name within the last day" + } + }, + "version": { + "type": "keyword", + "_meta": { + "description": "An array of the top 3 service runtime version within the last day" + } + }, + "composite": { + "type": "keyword", + "_meta": { + "description": "Composite field containing service runtime name and version sorted by doc count." + } + } + } + } + } + } + } } } }, diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/__snapshots__/tasks.test.ts.snap b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/__snapshots__/tasks.test.ts.snap index 0cfc50412dac4..e3bb9a2525782 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/__snapshots__/tasks.test.ts.snap +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/__snapshots__/tasks.test.ts.snap @@ -1,5 +1,1132 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`data telemetry collection tasks agents should return agent data per agent name 1`] = ` +Object { + "agents": Object { + "android/java": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "dotnet": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "go": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "iOS/swift": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "ios/swift": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "java": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "js-base": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "nodejs": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "opentelemetry/android": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/cpp": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/dotnet": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/erlang": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/go": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/java": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "opentelemetry/java/elastic": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "opentelemetry/nodejs": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/php": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/python": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/ruby": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/rust": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/swift": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "opentelemetry/webjs": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "otlp/android": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/cpp": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/dotnet": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/erlang": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/go": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/java": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "otlp/java/elastic": Object { + "agent": Object { + "activation_method": Array [ + "k8s-attach", + ], + "version": Array [ + "1.38.0", + ], + }, + "service": Object { + "framework": Object { + "composite": Array [ + "Spring Web MVC/1.10.1", + ], + "name": Array [ + "Spring Web MVC", + ], + "version": Array [ + "6.1.11", + ], + }, + "language": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + "runtime": Object { + "composite": Array [ + "Java/17.0.12", + ], + "name": Array [ + "Java", + ], + "version": Array [ + "17.0.12", + ], + }, + }, + }, + "otlp/nodejs": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/php": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/python": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/ruby": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/rust": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/swift": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "otlp/webjs": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "php": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "python": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "ruby": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + "rum-js": Object { + "agent": Object { + "activation_method": Array [], + "version": Array [], + }, + "service": Object { + "framework": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "language": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + "runtime": Object { + "composite": Array [], + "name": Array [], + "version": Array [], + }, + }, + }, + }, +} +`; + exports[`data telemetry collection tasks indices_stats returns a map of index stats 1`] = ` Object { "indices": Object { @@ -615,3 +1742,54 @@ Object { }, } `; + +exports[`data telemetry collection tasks services should return services per agent name 1`] = ` +Object { + "has_any_services": true, + "has_any_services_per_official_agent": true, + "services_per_agent": Object { + "android/java": 0, + "dotnet": 0, + "go": 0, + "iOS/swift": 0, + "ios/swift": 0, + "java": 10, + "js-base": 0, + "nodejs": 0, + "opentelemetry": 4, + "opentelemetry/android": 0, + "opentelemetry/cpp": 0, + "opentelemetry/dotnet": 0, + "opentelemetry/erlang": 0, + "opentelemetry/go": 0, + "opentelemetry/java": 5, + "opentelemetry/java/elastic": 6, + "opentelemetry/nodejs": 0, + "opentelemetry/php": 0, + "opentelemetry/python": 0, + "opentelemetry/ruby": 0, + "opentelemetry/rust": 0, + "opentelemetry/swift": 0, + "opentelemetry/webjs": 0, + "otlp": 1, + "otlp/android": 0, + "otlp/cpp": 0, + "otlp/dotnet": 0, + "otlp/erlang": 0, + "otlp/go": 0, + "otlp/java": 2, + "otlp/java/elastic": 3, + "otlp/nodejs": 0, + "otlp/php": 0, + "otlp/python": 0, + "otlp/ruby": 0, + "otlp/rust": 0, + "otlp/swift": 0, + "otlp/webjs": 0, + "php": 0, + "python": 0, + "ruby": 0, + "rum-js": 0, + }, +} +`; diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts index a2b6809f855e7..7bba841a8dee5 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.test.ts @@ -873,4 +873,150 @@ describe('data telemetry collection tasks', () => { }); }); }); + + describe('services', () => { + const task = tasks.find((t) => t.name === 'services'); + + it('should return services per agent name', async () => { + const search = jest.fn().mockImplementation((params: any) => { + const filter = params.body.query.bool.filter[0]; + const queryKnownAgentNames = filter.term; + const queryOtelAgentNames = filter.prefix; + const queryServices = filter.exists; + + if (queryKnownAgentNames && queryKnownAgentNames['agent.name'] === 'java') { + return Promise.resolve({ + aggregations: { + services: { + value: 10, + }, + }, + }); + } else if (queryOtelAgentNames) { + return Promise.resolve({ + aggregations: { + agent_name: { + buckets: [ + { + key: 'otlp', + services: { value: 1 }, + }, + { + key: 'otlp/java', + services: { value: 2 }, + }, + { + key: 'otlp/java/elastic', + services: { value: 3 }, + }, + { + key: 'opentelemetry', + services: { value: 4 }, + }, + { + key: 'opentelemetry/java', + services: { value: 5 }, + }, + { + key: 'opentelemetry/java/elastic', + services: { value: 6 }, + }, + ], + }, + }, + }); + } else if (queryServices) { + return Promise.resolve({ hits: { total: { value: 100 } } }); + } else { + return Promise.resolve({ aggregations: { services: { value: 0 } } }); + } + }); + + expect( + await task?.executor({ indices, telemetryClient: { search } } as any) + ).toMatchSnapshot(); + }); + }); + + describe('agents', () => { + const task = tasks.find((t) => t.name === 'agents'); + + it('should return agent data per agent name', async () => { + const search = jest.fn().mockImplementation((params: any) => { + const agentDataMock = { + 'agent.activation_method': { buckets: [{ key: 'k8s-attach' }] }, + 'agent.version': { buckets: [{ key: '1.38.0' }] }, + 'service.framework.name': { + buckets: [ + { + key: 'Spring Web MVC', + 'service.framework.version': { buckets: [{ key: '1.10.1' }] }, + }, + ], + }, + 'service.framework.version': { buckets: [{ key: '6.1.11', doc_count: 111 }] }, + 'service.language.name': { + buckets: [ + { + key: 'Java', + 'service.language.version': { buckets: [{ key: '17.0.12' }] }, + }, + ], + }, + 'service.language.version': { buckets: [{ key: '17.0.12', doc_count: 112 }] }, + 'service.runtime.name': { + buckets: [ + { + key: 'Java', + 'service.runtime.version': { buckets: [{ key: '17.0.12', doc_count: 113 }] }, + }, + ], + }, + 'service.runtime.version': { buckets: [{ key: '17.0.12', doc_count: 113 }] }, + }; + + const filter = params.body.query.bool.filter[0]; + const queryKnownAgentNames = filter.term; + const queryOtelAgentNames = filter.prefix; + + if (queryKnownAgentNames && queryKnownAgentNames['agent.name'] === 'java') { + return Promise.resolve({ + aggregations: agentDataMock, + }); + } else if (queryOtelAgentNames) { + return Promise.resolve({ + aggregations: { + agent_name: { + buckets: [ + { key: 'otlp', ...agentDataMock }, + { key: 'otlp/java', ...agentDataMock }, + { key: 'otlp/java/elastic', ...agentDataMock }, + { key: 'opentelemetry', ...agentDataMock }, + { key: 'opentelemetry/java', ...agentDataMock }, + { key: 'opentelemetry/java/elastic', ...agentDataMock }, + ], + }, + }, + }); + } else { + return Promise.resolve({ + aggregations: { + 'agent.activation_method': { buckets: [] }, + 'agent.version': { buckets: [] }, + 'service.framework.name': { buckets: [] }, + 'service.framework.version': { buckets: [] }, + 'service.language.name': { buckets: [] }, + 'service.language.version': { buckets: [] }, + 'service.runtime.name': { buckets: [] }, + 'service.runtime.version': { buckets: [] }, + }, + }); + } + }); + + expect( + await task?.executor({ indices, telemetryClient: { search } } as any) + ).toMatchSnapshot(); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts index 1347cbb4e3641..1ab2a6d44969b 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/collect_data_telemetry/tasks.ts @@ -8,11 +8,17 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { getKqlFieldNamesFromExpression } from '@kbn/es-query'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { createHash } from 'crypto'; -import { flatten, merge, pickBy, sortBy, sum, uniq } from 'lodash'; +import { flatten, merge, pickBy, sortBy, sum, uniq, without } from 'lodash'; import { SavedObjectsClient } from '@kbn/core/server'; import type { APMIndices } from '@kbn/apm-data-access-plugin/server'; +import { + AGENT_NAMES, + OPEN_TELEMETRY_AGENT_NAMES, + OPEN_TELEMETRY_BASE_AGENT_NAMES, + RUM_AGENT_NAMES, + type OpenTelemetryAgentName, +} from '@kbn/elastic-agent-utils/src/agent_names'; import { unflattenKnownApmEventFields } from '@kbn/apm-data-access-plugin/server/utils'; -import { AGENT_NAMES, RUM_AGENT_NAMES } from '../../../../common/agent_name'; import { AGENT_ACTIVATION_METHOD, AGENT_NAME, @@ -77,6 +83,7 @@ import { type ISavedObjectsClient = Pick; const TIME_RANGES = ['1d', 'all'] as const; +const AGENT_NAMES_WITHOUT_OTEL = without(AGENT_NAMES, ...OPEN_TELEMETRY_AGENT_NAMES); type TimeRange = (typeof TIME_RANGES)[number]; const range1d = { range: { '@timestamp': { gte: 'now-1d' } } }; @@ -608,42 +615,97 @@ export const tasks: TelemetryTask[] = [ { name: 'services', executor: async ({ indices, telemetryClient }) => { - const servicesPerAgent = await AGENT_NAMES.reduce((prevJob, agentName) => { - return prevJob.then(async (data) => { - const response = await telemetryClient.search({ - index: [indices.error, indices.span, indices.metric, indices.transaction], - body: { - size: 0, - track_total_hits: false, - timeout, - query: { - bool: { - filter: [ - { - term: { - [AGENT_NAME]: agentName, + const servicesPerAgentExcludingOtel = await AGENT_NAMES_WITHOUT_OTEL.reduce( + (prevJob, agentName) => { + return prevJob.then(async (data) => { + const response = await telemetryClient.search({ + index: [indices.error, indices.span, indices.metric, indices.transaction], + body: { + size: 0, + track_total_hits: false, + timeout, + query: { + bool: { + filter: [ + { + term: { + [AGENT_NAME]: agentName, + }, }, + range1d, + ], + }, + }, + aggs: { + services: { + cardinality: { + field: SERVICE_NAME, }, - range1d, - ], + }, }, }, - aggs: { - services: { - cardinality: { - field: SERVICE_NAME, + }); + + data[agentName] = response.aggregations?.services.value || 0; + return data; + }); + }, + Promise.resolve({} as Record) + ); + + const initOtelAgents = OPEN_TELEMETRY_AGENT_NAMES.reduce((acc, agent) => { + acc[agent] = 0; + return acc; + }, {} as Record); + + const servicesPerOtelAgents = await OPEN_TELEMETRY_BASE_AGENT_NAMES.reduce( + (prevJob, agentName) => { + return prevJob.then(async (data) => { + const response = await telemetryClient.search({ + index: [indices.error, indices.span, indices.metric, indices.transaction], + body: { + size: 0, + track_total_hits: false, + timeout, + query: { + bool: { + filter: [{ prefix: { [AGENT_NAME]: agentName } }, range1d], + }, + }, + aggs: { + agent_name: { + terms: { + field: AGENT_NAME, + size: 1000, + }, + aggs: { + services: { + cardinality: { + field: SERVICE_NAME, + }, + }, + }, }, }, }, - }, - }); + }); - return { - ...data, - [agentName]: response.aggregations?.services.value || 0, - }; - }); - }, Promise.resolve({} as Record)); + const aggregatedServicesPerAgents = response.aggregations?.agent_name.buckets.reduce( + (acc, agent) => { + acc[agent.key as OpenTelemetryAgentName] = agent.services.value || 0; + return acc; + }, + initOtelAgents + ); + + return { + ...data, + ...aggregatedServicesPerAgents, + }; + }); + }, + Promise.resolve(initOtelAgents) + ); const services = await telemetryClient.search({ index: [indices.error, indices.span, indices.metric, indices.transaction], @@ -667,10 +729,15 @@ export const tasks: TelemetryTask[] = [ }, }); + const servicesPerAgents: Record = { + ...servicesPerAgentExcludingOtel, + ...servicesPerOtelAgents, + }; + return { - has_any_services_per_official_agent: sum(Object.values(servicesPerAgent)) > 0, + has_any_services_per_official_agent: sum(Object.values(servicesPerAgents)) > 0, has_any_services: services?.hits?.total?.value > 0, - services_per_agent: servicesPerAgent, + services_per_agent: servicesPerAgents, }; }, }, @@ -900,113 +967,112 @@ export const tasks: TelemetryTask[] = [ name: 'agents', executor: async ({ indices, telemetryClient }) => { const size = 3; - - const agentData = await AGENT_NAMES.reduce(async (prevJob, agentName) => { - const data = await prevJob; - - const response = await telemetryClient.search({ - index: [indices.error, indices.metric, indices.transaction], - body: { - track_total_hits: false, - size: 0, - timeout, - query: { - bool: { - filter: [{ term: { [AGENT_NAME]: agentName } }, range1d], + const toComposite = (outerKey: string | number, innerKey: string | number) => + `${outerKey}/${innerKey}`; + const agentNameAggs = { + [AGENT_ACTIVATION_METHOD]: { + terms: { + field: AGENT_ACTIVATION_METHOD, + size, + }, + }, + [AGENT_VERSION]: { + terms: { + field: AGENT_VERSION, + size, + }, + }, + [SERVICE_FRAMEWORK_NAME]: { + terms: { + field: SERVICE_FRAMEWORK_NAME, + size, + }, + aggs: { + [SERVICE_FRAMEWORK_VERSION]: { + terms: { + field: SERVICE_FRAMEWORK_VERSION, + size, }, }, - sort: { - '@timestamp': 'desc', - }, - aggs: { - [AGENT_ACTIVATION_METHOD]: { - terms: { - field: AGENT_ACTIVATION_METHOD, - size, - }, - }, - [AGENT_VERSION]: { - terms: { - field: AGENT_VERSION, - size, - }, - }, - [SERVICE_FRAMEWORK_NAME]: { - terms: { - field: SERVICE_FRAMEWORK_NAME, - size, - }, - aggs: { - [SERVICE_FRAMEWORK_VERSION]: { - terms: { - field: SERVICE_FRAMEWORK_VERSION, - size, - }, - }, - }, - }, - [SERVICE_FRAMEWORK_VERSION]: { - terms: { - field: SERVICE_FRAMEWORK_VERSION, - size, - }, - }, - [SERVICE_LANGUAGE_NAME]: { - terms: { - field: SERVICE_LANGUAGE_NAME, - size, - }, - aggs: { - [SERVICE_LANGUAGE_VERSION]: { - terms: { - field: SERVICE_LANGUAGE_VERSION, - size, - }, - }, - }, + }, + }, + [SERVICE_FRAMEWORK_VERSION]: { + terms: { + field: SERVICE_FRAMEWORK_VERSION, + size, + }, + }, + [SERVICE_LANGUAGE_NAME]: { + terms: { + field: SERVICE_LANGUAGE_NAME, + size, + }, + aggs: { + [SERVICE_LANGUAGE_VERSION]: { + terms: { + field: SERVICE_LANGUAGE_VERSION, + size, }, - [SERVICE_LANGUAGE_VERSION]: { - terms: { - field: SERVICE_LANGUAGE_VERSION, - size, - }, + }, + }, + }, + [SERVICE_LANGUAGE_VERSION]: { + terms: { + field: SERVICE_LANGUAGE_VERSION, + size, + }, + }, + [SERVICE_RUNTIME_NAME]: { + terms: { + field: SERVICE_RUNTIME_NAME, + size, + }, + aggs: { + [SERVICE_RUNTIME_VERSION]: { + terms: { + field: SERVICE_RUNTIME_VERSION, + size, }, - [SERVICE_RUNTIME_NAME]: { - terms: { - field: SERVICE_RUNTIME_NAME, - size, - }, - aggs: { - [SERVICE_RUNTIME_VERSION]: { - terms: { - field: SERVICE_RUNTIME_VERSION, - size, - }, - }, + }, + }, + }, + [SERVICE_RUNTIME_VERSION]: { + terms: { + field: SERVICE_RUNTIME_VERSION, + size, + }, + }, + }; + + const agentDataWithoutOtel = await AGENT_NAMES_WITHOUT_OTEL.reduce( + async (prevJob, agentName) => { + const data = await prevJob; + + const response = await telemetryClient.search({ + index: [indices.error, indices.metric, indices.transaction], + body: { + track_total_hits: false, + size: 0, + timeout, + query: { + bool: { + filter: [{ term: { [AGENT_NAME]: agentName } }, range1d], }, }, - [SERVICE_RUNTIME_VERSION]: { - terms: { - field: SERVICE_RUNTIME_VERSION, - size, - }, + sort: { + '@timestamp': 'desc', }, + aggs: agentNameAggs, }, - }, - }); - - const { aggregations } = response; + }); - if (!aggregations) { - return data; - } + const { aggregations } = response; - const toComposite = (outerKey: string | number, innerKey: string | number) => - `${outerKey}/${innerKey}`; + if (!aggregations) { + return data; + } - return { - ...data, - [agentName]: { + data[agentName] = { agent: { activation_method: aggregations[AGENT_ACTIVATION_METHOD].buckets .map((bucket) => bucket.key as string) @@ -1081,12 +1147,167 @@ export const tasks: TelemetryTask[] = [ .map((composite) => composite.name), }, }, - }, - }; - }, Promise.resolve({} as APMTelemetry['agents'])); + }; + return data; + }, + Promise.resolve({} as NonNullable) + ); + + const agentDataWithOtel = await OPEN_TELEMETRY_BASE_AGENT_NAMES.reduce( + async (prevJob, agentName) => { + const data = await prevJob; + + const response = await telemetryClient.search({ + index: [indices.error, indices.metric, indices.transaction], + body: { + track_total_hits: false, + size: 0, + timeout, + query: { + bool: { + filter: [{ prefix: { [AGENT_NAME]: agentName } }, range1d], + }, + }, + sort: { + '@timestamp': 'desc', + }, + aggs: { + agent_name: { + terms: { + field: AGENT_NAME, + size: 1000, + }, + aggs: agentNameAggs, + }, + }, + }, + }); + + const { aggregations } = response; + + if (!aggregations) { + return data; + } + + const initAgentData = OPEN_TELEMETRY_AGENT_NAMES.reduce((acc, agent) => { + acc[agent] = { + agent: { + activation_method: [], + version: [], + }, + service: { + framework: { + name: [], + version: [], + composite: [], + }, + language: { + name: [], + version: [], + composite: [], + }, + runtime: { + name: [], + version: [], + composite: [], + }, + }, + }; + return acc; + }, {} as NonNullable); + + const agentData = aggregations?.agent_name.buckets.reduce((acc, agentNamesAggs) => { + acc[agentNamesAggs.key as OpenTelemetryAgentName] = { + agent: { + activation_method: agentNamesAggs[AGENT_ACTIVATION_METHOD].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + version: agentNamesAggs[AGENT_VERSION].buckets.map( + (bucket) => bucket.key as string + ), + }, + service: { + framework: { + name: agentNamesAggs[SERVICE_FRAMEWORK_NAME].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + version: agentNamesAggs[SERVICE_FRAMEWORK_VERSION].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + composite: sortBy( + flatten( + agentNamesAggs[SERVICE_FRAMEWORK_NAME].buckets.map((bucket) => + bucket[SERVICE_FRAMEWORK_VERSION].buckets.map((versionBucket) => ({ + doc_count: versionBucket.doc_count, + name: toComposite(bucket.key, versionBucket.key), + })) + ) + ), + 'doc_count' + ) + .reverse() + .slice(0, size) + .map((composite) => composite.name), + }, + language: { + name: agentNamesAggs[SERVICE_LANGUAGE_NAME].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + version: agentNamesAggs[SERVICE_LANGUAGE_VERSION].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + composite: sortBy( + flatten( + agentNamesAggs[SERVICE_LANGUAGE_NAME].buckets.map((bucket) => + bucket[SERVICE_LANGUAGE_VERSION].buckets.map((versionBucket) => ({ + doc_count: versionBucket.doc_count, + name: toComposite(bucket.key, versionBucket.key), + })) + ) + ), + 'doc_count' + ) + .reverse() + .slice(0, size) + .map((composite) => composite.name), + }, + runtime: { + name: agentNamesAggs[SERVICE_RUNTIME_NAME].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + version: agentNamesAggs[SERVICE_RUNTIME_VERSION].buckets + .map((bucket) => bucket.key as string) + .slice(0, size), + composite: sortBy( + flatten( + agentNamesAggs[SERVICE_RUNTIME_NAME].buckets.map((bucket) => + bucket[SERVICE_RUNTIME_VERSION].buckets.map((versionBucket) => ({ + doc_count: versionBucket.doc_count, + name: toComposite(bucket.key, versionBucket.key), + })) + ) + ), + 'doc_count' + ) + .reverse() + .slice(0, size) + .map((composite) => composite.name), + }, + }, + }; + return acc; + }, initAgentData); + + return { + ...data, + ...agentData, + }; + }, + Promise.resolve({} as APMTelemetry['agents']) + ); return { - agents: agentData, + agents: { ...agentDataWithoutOtel, ...agentDataWithOtel }, }; }, }, diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/schema.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/schema.ts index 917237963ef37..d351cc40ae721 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/schema.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/schema.ts @@ -7,7 +7,7 @@ import { MakeSchemaFrom } from '@kbn/usage-collection-plugin/server'; import { AggregatedTransactionsCounts, APMUsage, APMPerService, DataStreamCombined } from './types'; -import { ElasticAgentName } from '../../../typings/es_schemas/ui/fields/agent'; +import type { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; const aggregatedTransactionCountSchema: MakeSchemaFrom = { expected_metric_document_count: { @@ -63,7 +63,7 @@ const dataStreamCombinedSchema: MakeSchemaFrom = { }, }; -const agentSchema: MakeSchemaFrom['agents'][ElasticAgentName] = { +const agentSchema: MakeSchemaFrom['agents'][AgentName] = { agent: { version: { type: 'array', @@ -460,6 +460,35 @@ const apmPerAgentSchema: Pick, 'services_per_agen python: agentSchema, ruby: agentSchema, 'rum-js': agentSchema, + otlp: agentSchema, + opentelemetry: agentSchema, + 'opentelemetry/cpp': agentSchema, + 'opentelemetry/dotnet': agentSchema, + 'opentelemetry/erlang': agentSchema, + 'opentelemetry/go': agentSchema, + 'opentelemetry/java': agentSchema, + 'opentelemetry/nodejs': agentSchema, + 'opentelemetry/php': agentSchema, + 'opentelemetry/python': agentSchema, + 'opentelemetry/ruby': agentSchema, + 'opentelemetry/rust': agentSchema, + 'opentelemetry/swift': agentSchema, + 'opentelemetry/android': agentSchema, + 'opentelemetry/webjs': agentSchema, + 'otlp/cpp': agentSchema, + 'otlp/dotnet': agentSchema, + 'otlp/erlang': agentSchema, + 'otlp/go': agentSchema, + 'otlp/java': agentSchema, + 'otlp/nodejs': agentSchema, + 'otlp/php': agentSchema, + 'otlp/python': agentSchema, + 'otlp/ruby': agentSchema, + 'otlp/rust': agentSchema, + 'otlp/swift': agentSchema, + 'otlp/android': agentSchema, + 'otlp/webjs': agentSchema, + 'ios/swift': agentSchema, }, }; diff --git a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/types.ts b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/types.ts index 7ae7c14c8c88e..757b8bad533ba 100644 --- a/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/types.ts +++ b/x-pack/plugins/observability_solution/apm/server/lib/apm_telemetry/types.ts @@ -6,7 +6,7 @@ */ import { DeepPartial } from 'utility-types'; -import { AgentName, ElasticAgentName } from '@kbn/elastic-agent-utils'; +import type { AgentName } from '@kbn/elastic-agent-utils'; import { RollupInterval } from '../../../common/rollup'; export interface TimeframeMap { @@ -133,7 +133,7 @@ export interface APMUsage { }; }; agents: Record< - ElasticAgentName, + AgentName, { agent: { version: string[]; diff --git a/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx b/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx index 6f13c33585bca..7941881ff2c51 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/entity_enablement/enable_entity_model_button.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { EntityManagerUnauthorizedError } from '@kbn/entityManager-plugin/public'; import type { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; +import { TechnicalPreviewBadge } from '@kbn/observability-shared-plugin/public'; import { useKibana } from '../../hooks/use_kibana'; import { Unauthorized } from './unauthorized_modal'; @@ -57,6 +58,8 @@ export function EnableEntityModelButton({ onSuccess }: { onSuccess: () => void } data-test-subj="inventoryInventoryPageTemplateFilledButton" fill onClick={handleEnablement} + iconType={() => } + iconSide="right" > {i18n.translate('xpack.inventory.noData.card.button', { defaultMessage: 'Enable', diff --git a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx index 3b12e11d2ba7c..79db53f39c346 100644 --- a/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx +++ b/x-pack/plugins/observability_solution/inventory/public/components/inventory_page_template/no_data_config.tsx @@ -33,8 +33,20 @@ export function getEntityManagerEnablement({ description: ( + {i18n.translate('xpack.inventory.noData.card.description.inventory', { + defaultMessage: 'Inventory', + })} + + ), link: ( ; +export const AssociatedFilterType = z.enum([ + 'document_only', + 'saved_object_only', + 'document_and_saved_object', + 'orphan', +]); +export type AssociatedFilterTypeEnum = typeof AssociatedFilterType.enum; +export const AssociatedFilterTypeEnum = AssociatedFilterType.enum; + export type DocumentIds = z.infer; export const DocumentIds = z.union([z.array(z.string()), z.string()]); @@ -41,6 +54,7 @@ export const GetNotesRequestQuery = z.object({ sortOrder: z.string().nullable().optional(), filter: z.string().nullable().optional(), userFilter: z.string().nullable().optional(), + associatedFilter: AssociatedFilterType.optional(), }); export type GetNotesRequestQueryInput = z.input; diff --git a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml index cc8681c6f8f64..734a9580dcd23 100644 --- a/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/timeline/get_notes/get_notes_route.schema.yaml @@ -56,6 +56,10 @@ paths: schema: nullable: true type: string + - name: associatedFilter + in: query + schema: + $ref: '#/components/schemas/AssociatedFilterType' responses: '200': description: Indicates the requested notes were returned. @@ -68,6 +72,14 @@ paths: components: schemas: + AssociatedFilterType: + type: string + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + description: Filter notes based on their association with a document or saved object. DocumentIds: oneOf: - type: array diff --git a/x-pack/plugins/security_solution/common/notes/constants.ts b/x-pack/plugins/security_solution/common/notes/constants.ts new file mode 100644 index 0000000000000..c296e377d1c4f --- /dev/null +++ b/x-pack/plugins/security_solution/common/notes/constants.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export enum AssociatedFilter { + all = 'all', + documentOnly = 'document_only', + savedObjectOnly = 'saved_object_only', + documentAndSavedObject = 'document_and_saved_object', + orphan = 'orphan', +} diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml index 8de192ce26826..e48dafbdc0e05 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_timeline_api_2023_10_31.bundled.schema.yaml @@ -102,6 +102,10 @@ paths: schema: nullable: true type: string + - in: query + name: associatedFilter + schema: + $ref: '#/components/schemas/AssociatedFilterType' responses: '200': content: @@ -921,6 +925,14 @@ paths: - access:securitySolution components: schemas: + AssociatedFilterType: + description: Filter notes based on their association with a document or saved object. + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + type: string BareNote: type: object properties: diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml index 66127d5b8cd52..fab5e022c6b06 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_timeline_api_2023_10_31.bundled.schema.yaml @@ -102,6 +102,10 @@ paths: schema: nullable: true type: string + - in: query + name: associatedFilter + schema: + $ref: '#/components/schemas/AssociatedFilterType' responses: '200': content: @@ -921,6 +925,14 @@ paths: - access:securitySolution components: schemas: + AssociatedFilterType: + description: Filter notes based on their association with a document or saved object. + enum: + - document_only + - saved_object_only + - document_and_saved_object + - orphan + type: string BareNote: type: object properties: diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index 01eec48ed7718..5874062f05523 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -7,6 +7,7 @@ import { TableId } from '@kbn/securitysolution-data-table'; import type { DataViewSpec } from '@kbn/data-views-plugin/public'; +import { AssociatedFilter } from '../../../common/notes/constants'; import { ReqStatus } from '../../notes/store/notes.slice'; import { HostsFields } from '../../../common/api/search_strategy/hosts/model/sort'; import { InputsModelId } from '../store/inputs/constants'; @@ -550,6 +551,7 @@ export const mockGlobalState: State = { }, filter: '', userFilter: '', + associatedFilter: AssociatedFilter.all, search: '', selectedIds: [], pendingDeleteIds: [], diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.test.tsx index 0ce75c714f89f..fa22892f21db8 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { render, fireEvent, waitFor } from '@testing-library/react'; import { TestProviders } from '../../../../common/mock'; import { ScheduleRiskEngineCallout } from './schedule_risk_engine_callout'; +import { RiskEngineStatusEnum } from '../../../../../common/api/entity_analytics'; const oneHourFromNow = () => { const date = new Date(); @@ -48,7 +49,7 @@ describe('ScheduleRiskEngineCallout', () => { mockUseRiskEngineStatus.mockReturnValue({ data: { isNewRiskScoreModuleInstalled: true, - + risk_engine_status: RiskEngineStatusEnum.ENABLED, risk_engine_task_status: { status: 'idle', runAt: oneHourFromNow().toISOString(), @@ -70,7 +71,7 @@ describe('ScheduleRiskEngineCallout', () => { mockUseRiskEngineStatus.mockReturnValue({ data: { isNewRiskScoreModuleInstalled: true, - + risk_engine_status: RiskEngineStatusEnum.ENABLED, risk_engine_task_status: { status: 'running', runAt: oneHourFromNow().toISOString(), @@ -89,7 +90,7 @@ describe('ScheduleRiskEngineCallout', () => { mockUseRiskEngineStatus.mockReturnValue({ data: { isNewRiskScoreModuleInstalled: true, - + risk_engine_status: RiskEngineStatusEnum.ENABLED, risk_engine_task_status: { status: 'idle', runAt: new Date().toISOString(), // past date @@ -110,7 +111,7 @@ describe('ScheduleRiskEngineCallout', () => { mockUseRiskEngineStatus.mockReturnValueOnce({ data: { isNewRiskScoreModuleInstalled: true, - + risk_engine_status: RiskEngineStatusEnum.ENABLED, risk_engine_task_status: { status: 'idle', runAt: oneHourFromNow(), @@ -127,7 +128,7 @@ describe('ScheduleRiskEngineCallout', () => { mockUseRiskEngineStatus.mockReturnValueOnce({ data: { isNewRiskScoreModuleInstalled: true, - + risk_engine_status: RiskEngineStatusEnum.ENABLED, risk_engine_task_status: { status: 'idle', runAt: thirtyMinutesFromNow(), @@ -143,7 +144,7 @@ describe('ScheduleRiskEngineCallout', () => { mockUseRiskEngineStatus.mockReturnValue({ data: { isNewRiskScoreModuleInstalled: true, - + risk_engine_status: RiskEngineStatusEnum.ENABLED, risk_engine_task_status: { status: 'idle', runAt: new Date().toISOString(), // past date @@ -173,4 +174,19 @@ describe('ScheduleRiskEngineCallout', () => { expect(queryByTestId('risk-engine-callout')).toBeNull(); }); + + it('should not show the callout if the risk engine is disabled', () => { + mockUseRiskEngineStatus.mockReturnValue({ + data: { + isNewRiskScoreModuleInstalled: true, + risk_engine_status: RiskEngineStatusEnum.DISABLED, + }, + }); + + const { queryByTestId } = render(, { + wrapper: TestProviders, + }); + + expect(queryByTestId('risk-engine-callout')).toBeNull(); + }); }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx index 2e8722cd20005..432e03c231a4d 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality_file_uploader/components/schedule_risk_engine_callout.tsx @@ -16,6 +16,7 @@ import React, { useCallback, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; +import { RiskEngineStatusEnum } from '../../../../../common/api/entity_analytics'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { formatTimeFromNow } from '../helpers'; import { useScheduleNowRiskEngineMutation } from '../../../api/hooks/use_schedule_now_risk_engine_mutation'; @@ -76,7 +77,10 @@ export const ScheduleRiskEngineCallout: React.FC = () => { scheduleRiskEngineMutation(); }, [scheduleRiskEngineMutation]); - if (!riskEngineStatus?.isNewRiskScoreModuleInstalled) { + if ( + !riskEngineStatus?.isNewRiskScoreModuleInstalled || + riskEngineStatus?.risk_engine_status !== RiskEngineStatusEnum.ENABLED + ) { return null; } diff --git a/x-pack/plugins/security_solution/public/notes/api/api.ts b/x-pack/plugins/security_solution/public/notes/api/api.ts index 3bac1a0a2d7df..917974a154884 100644 --- a/x-pack/plugins/security_solution/public/notes/api/api.ts +++ b/x-pack/plugins/security_solution/public/notes/api/api.ts @@ -11,6 +11,7 @@ import type { GetNotesResponse, PersistNoteRouteResponse, } from '../../../common/api/timeline'; +import type { AssociatedFilter } from '../../../common/notes/constants'; import { KibanaServices } from '../../common/lib/kibana'; import { NOTE_URL } from '../../../common/constants'; @@ -43,6 +44,7 @@ export const fetchNotes = async ({ sortOrder, filter, userFilter, + associatedFilter, search, }: { page: number; @@ -51,6 +53,7 @@ export const fetchNotes = async ({ sortOrder: string; filter: string; userFilter: string; + associatedFilter: AssociatedFilter; search: string; }) => { const response = await KibanaServices.get().http.get(NOTE_URL, { @@ -61,6 +64,7 @@ export const fetchNotes = async ({ sortOrder, filter, userFilter, + associatedFilter, search, }, version: '2023-10-31', diff --git a/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx b/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx index 71693edb81724..be9546c77525b 100644 --- a/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/search_row.test.tsx @@ -9,7 +9,8 @@ import { fireEvent, render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { SearchRow } from './search_row'; -import { SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids'; +import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids'; +import { AssociatedFilter } from '../../../common/notes/constants'; import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users'; jest.mock('../../common/components/user_profiles/use_suggest_users'); @@ -38,6 +39,7 @@ describe('SearchRow', () => { expect(getByTestId(SEARCH_BAR_TEST_ID)).toBeInTheDocument(); expect(getByTestId(USER_SELECT_TEST_ID)).toBeInTheDocument(); + expect(getByTestId(ASSOCIATED_NOT_SELECT_TEST_ID)).toBeInTheDocument(); }); it('should call the correct action when entering a value in the search bar', async () => { @@ -62,4 +64,13 @@ describe('SearchRow', () => { expect(mockDispatch).toHaveBeenCalled(); }); + + it('should call the correct action when select a value in the associated note dropdown', async () => { + const { getByTestId } = render(); + + const associatedNoteSelect = getByTestId(ASSOCIATED_NOT_SELECT_TEST_ID); + await userEvent.selectOptions(associatedNoteSelect, [AssociatedFilter.documentOnly]); + + expect(mockDispatch).toHaveBeenCalled(); + }); }); diff --git a/x-pack/plugins/security_solution/public/notes/components/search_row.tsx b/x-pack/plugins/security_solution/public/notes/components/search_row.tsx index 9a33c84cbec58..d540a586814d8 100644 --- a/x-pack/plugins/security_solution/public/notes/components/search_row.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/search_row.tsx @@ -5,30 +5,48 @@ * 2.0. */ -import { EuiComboBox, EuiFlexGroup, EuiFlexItem, EuiSearchBar } from '@elastic/eui'; import React, { useMemo, useCallback, useState } from 'react'; +import type { EuiSelectOption } from '@elastic/eui'; +import { + EuiComboBox, + EuiFlexGroup, + EuiFlexItem, + EuiSearchBar, + EuiSelect, + useGeneratedHtmlId, +} from '@elastic/eui'; import { useDispatch } from 'react-redux'; import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; import { i18n } from '@kbn/i18n'; import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; -import { SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids'; +import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids'; import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users'; -import { userFilterUsers, userSearchedNotes } from '..'; +import { userFilterAssociatedNotes, userFilterUsers, userSearchedNotes } from '..'; +import { AssociatedFilter } from '../../../common/notes/constants'; export const USERS_DROPDOWN = i18n.translate('xpack.securitySolution.notes.usersDropdownLabel', { defaultMessage: 'Users', }); +const FILTER_SELECT = i18n.translate('xpack.securitySolution.notes.management.filterSelect', { + defaultMessage: 'Select filter', +}); + +const searchBox = { + placeholder: 'Search note contents', + incremental: false, + 'data-test-subj': SEARCH_BAR_TEST_ID, +}; +const associatedNoteSelectOptions: EuiSelectOption[] = [ + { value: AssociatedFilter.all, text: 'All' }, + { value: AssociatedFilter.documentOnly, text: 'Attached to document only' }, + { value: AssociatedFilter.savedObjectOnly, text: 'Attached to timeline only' }, + { value: AssociatedFilter.documentAndSavedObject, text: 'Attached to document and timeline' }, + { value: AssociatedFilter.orphan, text: 'Orphan' }, +]; export const SearchRow = React.memo(() => { const dispatch = useDispatch(); - const searchBox = useMemo( - () => ({ - placeholder: 'Search note contents', - incremental: false, - 'data-test-subj': SEARCH_BAR_TEST_ID, - }), - [] - ); + const associatedSelectId = useGeneratedHtmlId({ prefix: 'associatedSelectId' }); const onQueryChange = useCallback( ({ queryText }: { queryText: string }) => { @@ -57,6 +75,13 @@ export const SearchRow = React.memo(() => { [dispatch] ); + const onAssociatedNoteSelectChange = useCallback( + (e: React.ChangeEvent) => { + dispatch(userFilterAssociatedNotes(e.target.value as AssociatedFilter)); + }, + [dispatch] + ); + return ( @@ -73,6 +98,16 @@ export const SearchRow = React.memo(() => { data-test-subj={USER_SELECT_TEST_ID} /> + + + ); }); diff --git a/x-pack/plugins/security_solution/public/notes/components/test_ids.ts b/x-pack/plugins/security_solution/public/notes/components/test_ids.ts index 1464ed17d8764..e056ca19d6a2e 100644 --- a/x-pack/plugins/security_solution/public/notes/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/notes/components/test_ids.ts @@ -21,3 +21,4 @@ export const NOTE_CONTENT_BUTTON_TEST_ID = `${PREFIX}NoteContentButton` as const export const NOTE_CONTENT_POPOVER_TEST_ID = `${PREFIX}NoteContentPopover` as const; export const SEARCH_BAR_TEST_ID = `${PREFIX}SearchBar` as const; export const USER_SELECT_TEST_ID = `${PREFIX}UserSelect` as const; +export const ASSOCIATED_NOT_SELECT_TEST_ID = `${PREFIX}AssociatedNoteSelect` as const; diff --git a/x-pack/plugins/security_solution/public/notes/components/utility_bar.tsx b/x-pack/plugins/security_solution/public/notes/components/utility_bar.tsx index e34824d1ad814..83c581507a2f9 100644 --- a/x-pack/plugins/security_solution/public/notes/components/utility_bar.tsx +++ b/x-pack/plugins/security_solution/public/notes/components/utility_bar.tsx @@ -24,6 +24,7 @@ import { selectNotesTableSearch, userSelectedBulkDelete, selectNotesTableUserFilters, + selectNotesTableAssociatedFilter, } from '..'; export const BATCH_ACTIONS = i18n.translate( @@ -53,6 +54,7 @@ export const NotesUtilityBar = React.memo(() => { const sort = useSelector(selectNotesTableSort); const selectedItems = useSelector(selectNotesTableSelectedIds); const notesUserFilters = useSelector(selectNotesTableUserFilters); + const notesAssociatedFilters = useSelector(selectNotesTableAssociatedFilter); const resultsCount = useMemo(() => { const { perPage, page, total } = pagination; const startOfCurrentPage = perPage * (page - 1) + 1; @@ -86,6 +88,7 @@ export const NotesUtilityBar = React.memo(() => { sortOrder: sort.direction, filter: '', userFilter: notesUserFilters, + associatedFilter: notesAssociatedFilters, search: notesSearch, }) ); @@ -96,6 +99,7 @@ export const NotesUtilityBar = React.memo(() => { sort.field, sort.direction, notesUserFilters, + notesAssociatedFilters, notesSearch, ]); return ( diff --git a/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx b/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx index e329f0d75b911..4795d6146be4d 100644 --- a/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx +++ b/x-pack/plugins/security_solution/public/notes/pages/note_management_page.tsx @@ -37,6 +37,7 @@ import { selectFetchNotesError, ReqStatus, selectNotesTableUserFilters, + selectNotesTableAssociatedFilter, } from '..'; import type { NotesState } from '..'; import { SearchRow } from '../components/search_row'; @@ -121,6 +122,7 @@ export const NoteManagementPage = () => { const sort = useSelector(selectNotesTableSort); const notesSearch = useSelector(selectNotesTableSearch); const notesUserFilters = useSelector(selectNotesTableUserFilters); + const notesAssociatedFilters = useSelector(selectNotesTableAssociatedFilter); const pendingDeleteIds = useSelector(selectNotesTablePendingDeleteIds); const isDeleteModalVisible = pendingDeleteIds.length > 0; const fetchNotesStatus = useSelector(selectFetchNotesStatus); @@ -137,6 +139,7 @@ export const NoteManagementPage = () => { sortOrder: sort.direction, filter: '', userFilter: notesUserFilters, + associatedFilter: notesAssociatedFilters, search: notesSearch, }) ); @@ -147,6 +150,7 @@ export const NoteManagementPage = () => { sort.field, sort.direction, notesUserFilters, + notesAssociatedFilters, notesSearch, ]); diff --git a/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts b/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts index 7cbaecf7d7135..65fa293bd824a 100644 --- a/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts +++ b/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts @@ -5,13 +5,15 @@ * 2.0. */ import * as uuid from 'uuid'; -import { miniSerializeError } from '@reduxjs/toolkit'; import type { SerializedError } from '@reduxjs/toolkit'; +import { miniSerializeError } from '@reduxjs/toolkit'; +import type { NotesState } from './notes.slice'; import { createNote, deleteNotes, - fetchNotesByDocumentIds, fetchNotes, + fetchNotesByDocumentIds, + fetchNotesBySavedObjectIds, initialNotesState, notesReducer, ReqStatus, @@ -20,6 +22,7 @@ import { selectCreateNoteStatus, selectDeleteNotesError, selectDeleteNotesStatus, + selectDocumentNotesBySavedObjectId, selectFetchNotesByDocumentIdsError, selectFetchNotesByDocumentIdsStatus, selectFetchNotesError, @@ -27,12 +30,16 @@ import { selectNoteById, selectNoteIds, selectNotesByDocumentId, - selectDocumentNotesBySavedObjectId, + selectNotesBySavedObjectId, selectNotesPagination, selectNotesTablePendingDeleteIds, selectNotesTableSearch, selectNotesTableSelectedIds, selectNotesTableSort, + selectSortedNotesByDocumentId, + selectSortedNotesBySavedObjectId, + selectNotesTableUserFilters, + selectNotesTableAssociatedFilter, userClosedDeleteModal, userFilteredNotes, userSearchedNotes, @@ -42,17 +49,13 @@ import { userSelectedRow, userSelectedNotesForDeletion, userSortedNotes, - selectSortedNotesByDocumentId, - fetchNotesBySavedObjectIds, - selectNotesBySavedObjectId, - selectSortedNotesBySavedObjectId, userFilterUsers, - selectNotesTableUserFilters, userClosedCreateErrorToast, + userFilterAssociatedNotes, } from './notes.slice'; -import type { NotesState } from './notes.slice'; import { mockGlobalState } from '../../common/mock'; import type { Note } from '../../../common/api/timeline'; +import { AssociatedFilter } from '../../../common/notes/constants'; const initalEmptyState = initialNotesState; @@ -102,6 +105,7 @@ const initialNonEmptyState: NotesState = { }, filter: '', userFilter: '', + associatedFilter: AssociatedFilter.all, search: '', selectedIds: [], pendingDeleteIds: [], @@ -515,6 +519,17 @@ describe('notesSlice', () => { }); }); + describe('userFilterAssociatedNotes', () => { + it('should set correct value to filter associated notes', () => { + const action = { type: userFilterAssociatedNotes.type, payload: 'abc' }; + + expect(notesReducer(initalEmptyState, action)).toEqual({ + ...initalEmptyState, + associatedFilter: 'abc', + }); + }); + }); + describe('userSearchedNotes', () => { it('should set correct value to search notes', () => { const action = { type: userSearchedNotes.type, payload: 'abc' }; @@ -851,7 +866,7 @@ describe('notesSlice', () => { expect(selectNotesTableSearch(state)).toBe('test search'); }); - it('should select associated filter', () => { + it('should select user filter', () => { const state = { ...mockGlobalState, notes: { ...initialNotesState, userFilter: 'abc' }, @@ -859,6 +874,14 @@ describe('notesSlice', () => { expect(selectNotesTableUserFilters(state)).toBe('abc'); }); + it('should select associated filter', () => { + const state = { + ...mockGlobalState, + notes: { ...initialNotesState, associatedFilter: AssociatedFilter.all }, + }; + expect(selectNotesTableAssociatedFilter(state)).toBe(AssociatedFilter.all); + }); + it('should select notes table pending delete ids', () => { const state = { ...mockGlobalState, diff --git a/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts b/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts index d5a4e7d4ab14e..28bf609a4f210 100644 --- a/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts +++ b/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts @@ -8,6 +8,7 @@ import type { EntityState, SerializedError } from '@reduxjs/toolkit'; import { createAsyncThunk, createEntityAdapter, createSlice } from '@reduxjs/toolkit'; import { createSelector } from 'reselect'; +import { AssociatedFilter } from '../../../common/notes/constants'; import type { State } from '../../common/store'; import { createNote as createNoteApi, @@ -59,6 +60,7 @@ export interface NotesState extends EntityState { filter: string; userFilter: string; search: string; + associatedFilter: AssociatedFilter; selectedIds: string[]; pendingDeleteIds: string[]; } @@ -93,6 +95,7 @@ export const initialNotesState: NotesState = notesAdapter.getInitialState({ }, filter: '', userFilter: '', + associatedFilter: AssociatedFilter.all, search: '', selectedIds: [], pendingDeleteIds: [], @@ -127,11 +130,13 @@ export const fetchNotes = createAsyncThunk< sortOrder: string; filter: string; userFilter: string; + associatedFilter: AssociatedFilter; search: string; }, {} >('notes/fetchNotes', async (args) => { - const { page, perPage, sortField, sortOrder, filter, userFilter, search } = args; + const { page, perPage, sortField, sortOrder, filter, userFilter, associatedFilter, search } = + args; const res = await fetchNotesApi({ page, perPage, @@ -139,6 +144,7 @@ export const fetchNotes = createAsyncThunk< sortOrder, filter, userFilter, + associatedFilter, search, }); return { @@ -163,7 +169,7 @@ export const deleteNotes = createAsyncThunk { state.userFilter = action.payload; }, + userFilterAssociatedNotes: (state: NotesState, action: { payload: AssociatedFilter }) => { + state.associatedFilter = action.payload; + }, userSearchedNotes: (state: NotesState, action: { payload: string }) => { state.search = action.payload; }, @@ -324,6 +334,8 @@ export const selectNotesTableSearch = (state: State) => state.notes.search; export const selectNotesTableUserFilters = (state: State) => state.notes.userFilter; +export const selectNotesTableAssociatedFilter = (state: State) => state.notes.associatedFilter; + export const selectNotesTablePendingDeleteIds = (state: State) => state.notes.pendingDeleteIds; export const selectFetchNotesError = (state: State) => state.notes.error.fetchNotes; @@ -412,6 +424,7 @@ export const { userSortedNotes, userFilteredNotes, userFilterUsers, + userFilterAssociatedNotes, userSearchedNotes, userSelectedRow, userClosedDeleteModal, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts index 6699e160634fd..32cb52a61d469 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/entity_store/united_entity_definitions/get_united_definition.ts @@ -48,9 +48,7 @@ export const getUnitedEntityDefinition = memoize( namespace, indexPatterns, }); - }, - ({ entityType, namespace, fieldHistoryLength }: Options) => - `${entityType}-${namespace}-${fieldHistoryLength}` + } ); export const getUnitedEntityDefinitionVersion = (entityType: EntityType): string => diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts index bc6c83e2b159c..7b8c732ae54ca 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/notes/get_notes.ts @@ -9,9 +9,13 @@ import type { IKibanaResponse } from '@kbn/core-http-server'; import { transformError } from '@kbn/securitysolution-es-utils'; import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { buildRouteValidationWithZod } from '@kbn/zod-helpers'; -import type { SavedObjectsFindOptions } from '@kbn/core-saved-objects-api-server'; -import { nodeBuilder } from '@kbn/es-query'; +import type { + SavedObjectsFindOptions, + SavedObjectsFindOptionsReference, +} from '@kbn/core-saved-objects-api-server'; import type { KueryNode } from '@kbn/es-query'; +import { nodeBuilder, nodeTypes } from '@kbn/es-query'; +import { AssociatedFilter } from '../../../../../common/notes/constants'; import { timelineSavedObjectType } from '../../saved_object_mappings'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { MAX_UNASSOCIATED_NOTES, NOTE_URL } from '../../../../../common/constants'; @@ -22,6 +26,7 @@ import { getAllSavedNote } from '../../saved_object/notes'; import { noteSavedObjectType } from '../../saved_object_mappings/notes'; import { GetNotesRequestQuery, type GetNotesResponse } from '../../../../../common/api/timeline'; +/* eslint-disable complexity */ export const getNotesRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .get({ @@ -128,21 +133,70 @@ export const getNotesRoute = (router: SecuritySolutionPluginRouter) => { filter, }; + // we need to combine the associatedFilter with the filter query + // we have to type case here because the filter is a string (from the schema) and that cannot be changed as it would be a breaking change + const filterAsKueryNode: KueryNode = (filter || '') as unknown as KueryNode; + const filterKueryNodeArray = [filterAsKueryNode]; + // retrieve all the notes created by a specific user const userFilter = queryParams?.userFilter; if (userFilter) { - // we need to combine the associatedFilter with the filter query - // we have to type case here because the filter is a string (from the schema) and that cannot be changed as it would be a breaking change - const filterAsKueryNode: KueryNode = (filter || '') as unknown as KueryNode; - - options.filter = nodeBuilder.and([ - nodeBuilder.is(`${noteSavedObjectType}.attributes.createdBy`, userFilter), - filterAsKueryNode, - ]); - } else { - options.filter = filter; + filterKueryNodeArray.push( + nodeBuilder.is(`${noteSavedObjectType}.attributes.createdBy`, userFilter) + ); + } + + const associatedFilter = queryParams?.associatedFilter; + if (associatedFilter) { + // select documents that have or don't have a reference to an empty value + // used in combination with hasReference (not associated with a timeline) or hasNoReference (associated with a timeline) + const referenceToATimeline: SavedObjectsFindOptionsReference = { + type: timelineSavedObjectType, + id: '', + }; + + // select documents that don't have a value in the eventId field (not associated with a document) + const emptyDocumentIdFilter: KueryNode = nodeBuilder.is( + `${noteSavedObjectType}.attributes.eventId`, + '' + ); + + switch (associatedFilter) { + case AssociatedFilter.documentOnly: + // select documents that have a reference to an empty saved object id (not associated with a timeline) + // and have a value in the eventId field (associated with a document) + options.hasReference = referenceToATimeline; + filterKueryNodeArray.push( + nodeTypes.function.buildNode('not', emptyDocumentIdFilter) + ); + break; + case AssociatedFilter.savedObjectOnly: + // select documents that don't have a reference to an empty saved object id (associated with a timeline) + // and don't have a value in the eventId field (not associated with a document) + options.hasNoReference = referenceToATimeline; + filterKueryNodeArray.push(emptyDocumentIdFilter); + break; + case AssociatedFilter.documentAndSavedObject: + // select documents that don't have a reference to an empty saved object id (associated with a timeline) + // and have a value in the eventId field (associated with a document) + options.hasNoReference = referenceToATimeline; + filterKueryNodeArray.push( + nodeTypes.function.buildNode('not', emptyDocumentIdFilter) + ); + break; + case AssociatedFilter.orphan: + // select documents that have a reference to an empty saved object id (not associated with a timeline) + // and don't have a value in the eventId field (not associated with a document) + options.hasReference = referenceToATimeline; + // TODO we might want to also check for the existence of the eventId field, on top of getting eventId having empty values + filterKueryNodeArray.push(emptyDocumentIdFilter); + break; + } } + // combine all filters + options.filter = nodeBuilder.and(filterKueryNodeArray); + const res = await getAllSavedNote(frameworkRequest, options); const body: GetNotesResponse = res ?? {}; return response.ok({ body }); diff --git a/x-pack/plugins/spaces/public/management/edit_space/edit_space_content_tab.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/edit_space_content_tab.test.tsx index bb55cea5cd50f..f586f0d7f035e 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/edit_space_content_tab.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/edit_space_content_tab.test.tsx @@ -19,12 +19,13 @@ import { import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { EditSpaceContentTab } from './edit_space_content_tab'; -import { EditSpaceProvider } from './provider'; +import { EditSpaceProviderRoot } from './provider'; import type { Space } from '../../../common'; import { spacesManagerMock } from '../../spaces_manager/spaces_manager.mock'; import type { SpaceContentTypeSummaryItem } from '../../types'; import { getPrivilegeAPIClientMock } from '../privilege_api_client.mock'; import { getRolesAPIClientMock } from '../roles_api_client.mock'; +import { getSecurityLicenseMock } from '../security_license.mock'; const getUrlForApp = (appId: string) => appId; const navigateToUrl = jest.fn(); @@ -42,7 +43,7 @@ const logger = loggingSystemMock.createLogger(); const TestComponent: React.FC = ({ children }) => { return ( - = ({ children }) => { notifications={notifications} overlays={overlays} getPrivilegesAPIClient={getPrivilegeAPIClient} + getSecurityLicense={getSecurityLicenseMock} theme={theme} i18n={i18n} logger={logger} > {children} - + ); }; diff --git a/x-pack/plugins/spaces/public/management/edit_space/edit_space_general_tab.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/edit_space_general_tab.test.tsx index 1c32b97f777c0..9a35572254340 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/edit_space_general_tab.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/edit_space_general_tab.test.tsx @@ -23,12 +23,13 @@ import { KibanaFeature } from '@kbn/features-plugin/common'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { EditSpaceSettingsTab } from './edit_space_general_tab'; -import { EditSpaceProvider } from './provider/edit_space_provider'; +import { EditSpaceProviderRoot } from './provider/edit_space_provider'; import type { SolutionView } from '../../../common'; import { SOLUTION_VIEW_CLASSIC } from '../../../common/constants'; import { spacesManagerMock } from '../../spaces_manager/spaces_manager.mock'; import { getPrivilegeAPIClientMock } from '../privilege_api_client.mock'; import { getRolesAPIClientMock } from '../roles_api_client.mock'; +import { getSecurityLicenseMock } from '../security_license.mock'; const space = { id: 'default', name: 'Default', disabledFeatures: [], _reserved: true }; const history = scopedHistoryMock.create(); @@ -64,7 +65,7 @@ describe('EditSpaceSettings', () => { const TestComponent: React.FC = ({ children }) => { return ( - { notifications={notifications} overlays={overlays} getPrivilegesAPIClient={getPrivilegeAPIClient} + getSecurityLicense={getSecurityLicenseMock} theme={theme} i18n={i18n} logger={logger} > {children} - + ); }; diff --git a/x-pack/plugins/spaces/public/management/edit_space/edit_space_page.tsx b/x-pack/plugins/spaces/public/management/edit_space/edit_space_page.tsx index 882301d36459a..bf59f00b5490d 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/edit_space_page.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/edit_space_page.tsx @@ -9,9 +9,9 @@ import React from 'react'; import type { ComponentProps, PropsWithChildren } from 'react'; import { EditSpace } from './edit_space'; -import { EditSpaceProvider, type EditSpaceProviderProps } from './provider'; +import { EditSpaceProviderRoot, type EditSpaceProviderRootProps } from './provider'; -type EditSpacePageProps = ComponentProps & EditSpaceProviderProps; +type EditSpacePageProps = ComponentProps & EditSpaceProviderRootProps; export function EditSpacePage({ spaceId, @@ -25,7 +25,7 @@ export function EditSpacePage({ ...editSpaceServicesProps }: PropsWithChildren) { return ( - + - + ); } diff --git a/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.test.tsx index fccd999eb7941..1959d1d8465ac 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.test.tsx @@ -19,10 +19,11 @@ import { import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { EditSpaceAssignedRolesTab } from './edit_space_roles_tab'; -import { EditSpaceProvider } from './provider'; +import { EditSpaceProviderRoot } from './provider'; import { spacesManagerMock } from '../../spaces_manager/spaces_manager.mock'; import { getPrivilegeAPIClientMock } from '../privilege_api_client.mock'; import { getRolesAPIClientMock } from '../roles_api_client.mock'; +import { getSecurityLicenseMock } from '../security_license.mock'; const getUrlForApp = (appId: string) => appId; const navigateToUrl = jest.fn(); @@ -51,7 +52,7 @@ describe('EditSpaceAssignedRolesTab', () => { const TestComponent: React.FC = ({ children }) => { return ( - { notifications={notifications} overlays={overlays} getPrivilegesAPIClient={getPrivilegeAPIClient} + getSecurityLicense={getSecurityLicenseMock} theme={theme} i18n={i18n} logger={logger} > {children} - + ); }; diff --git a/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.tsx b/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.tsx index 2733790d8de8b..2e3d40527dbd7 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/edit_space_roles_tab.tsx @@ -62,7 +62,7 @@ export const EditSpaceAssignedRolesTab: FC = ({ space, features, isReadOn (defaultSelected?: Role[]) => { const overlayRef = overlays.openFlyout( toMountPoint( - + = ({ space, features, isReadOn [ overlays, services, + dispatch, + state, space, features, - dispatch, invokeClient, getUrlForApp, theme, diff --git a/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.test.tsx index bfd7d7b6059e8..a236b9bc05e1d 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.test.tsx @@ -20,10 +20,15 @@ import { import type { ApplicationStart } from '@kbn/core-application-browser'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { EditSpaceProvider, useEditSpaceServices, useEditSpaceStore } from './edit_space_provider'; +import { + EditSpaceProviderRoot, + useEditSpaceServices, + useEditSpaceStore, +} from './edit_space_provider'; import { spacesManagerMock } from '../../../spaces_manager/spaces_manager.mock'; import { getPrivilegeAPIClientMock } from '../../privilege_api_client.mock'; import { getRolesAPIClientMock } from '../../roles_api_client.mock'; +import { getSecurityLicenseMock } from '../../security_license.mock'; const http = httpServiceMock.createStartContract(); const notifications = notificationServiceMock.createStartContract(); @@ -45,7 +50,7 @@ const SUTProvider = ({ }: PropsWithChildren>>) => { return ( - _, getRolesAPIClient: getRolesAPIClientMock, getPrivilegesAPIClient: getPrivilegeAPIClientMock, + getSecurityLicense: getSecurityLicenseMock, navigateToUrl: jest.fn(), capabilities, }} > {children} - + ); }; diff --git a/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.tsx b/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.tsx index 75af2beea2108..374d90d19ace1 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/provider/edit_space_provider.tsx @@ -23,6 +23,7 @@ import type { Logger } from '@kbn/logging'; import type { PrivilegesAPIClientPublicContract, RolesAPIClient, + SecurityLicense, } from '@kbn/security-plugin-types-public'; import { @@ -32,7 +33,7 @@ import { } from './reducers'; import type { SpacesManager } from '../../../spaces_manager'; -export interface EditSpaceProviderProps +export interface EditSpaceProviderRootProps extends Pick { logger: Logger; capabilities: ApplicationStart['capabilities']; @@ -42,10 +43,7 @@ export interface EditSpaceProviderProps spacesManager: SpacesManager; getRolesAPIClient: () => Promise; getPrivilegesAPIClient: () => Promise; -} - -export interface EditSpaceServices extends EditSpaceProviderProps { - invokeClient(arg: (clients: EditSpaceClients) => Promise): Promise; + getSecurityLicense: () => Promise; } interface EditSpaceClients { @@ -54,6 +52,15 @@ interface EditSpaceClients { privilegesClient: PrivilegesAPIClientPublicContract; } +export interface EditSpaceServices + extends Omit< + EditSpaceProviderRootProps, + 'getRolesAPIClient' | 'getPrivilegesAPIClient' | 'getSecurityLicense' + > { + invokeClient(arg: (clients: EditSpaceClients) => Promise): Promise; + license?: SecurityLicense; +} + export interface EditSpaceStore { state: IEditSpaceStoreState; dispatch: Dispatch; @@ -63,16 +70,43 @@ const createSpaceRolesContext = once(() => createContext( const createEditSpaceServicesContext = once(() => createContext(null)); +/** + * + * @description EditSpaceProvider is a provider component that wraps the children components with the necessary context providers for the Edit Space feature. It provides the necessary services and state management for the feature, + * this is provided as an export for use with out of band renders within the spaces app + */ export const EditSpaceProvider = ({ children, + state, + dispatch, ...services -}: PropsWithChildren) => { +}: PropsWithChildren) => { const EditSpaceStoreContext = createSpaceRolesContext(); const EditSpaceServicesContext = createEditSpaceServicesContext(); - const clients = useRef( - Promise.all([services.getRolesAPIClient(), services.getPrivilegesAPIClient()]) + return ( + + + {children} + + ); +}; + +/** + * @description EditSpaceProviderRoot is the root provider for the Edit Space feature. It instantiates the necessary services and state management for the feature. It ideally + * should only be rendered once + */ +export const EditSpaceProviderRoot = ({ + children, + ...services +}: PropsWithChildren) => { + const { logger, getRolesAPIClient, getPrivilegesAPIClient, getSecurityLicense } = services; + + const clients = useRef(Promise.all([getRolesAPIClient(), getPrivilegesAPIClient()])); + const license = useRef(getSecurityLicense); + + const licenseRef = useRef(); const rolesAPIClientRef = useRef(); const privilegesClientRef = useRef(); @@ -81,7 +115,14 @@ export const EditSpaceProvider = ({ fetchRolesError: false, }); - const { logger } = services; + const resolveSecurityLicense = useCallback(async () => { + try { + licenseRef.current = await license.current(); + } catch (err) { + logger.error('Could not resolve Security License!', err); + } + }, [logger]); + const resolveAPIClients = useCallback(async () => { try { [rolesAPIClientRef.current, privilegesClientRef.current] = await clients.current; @@ -94,6 +135,10 @@ export const EditSpaceProvider = ({ resolveAPIClients(); }, [resolveAPIClients]); + useEffect(() => { + resolveSecurityLicense(); + }, [resolveSecurityLicense]); + const createInitialState = useCallback((state: IEditSpaceStoreState) => { return state; }, []); @@ -118,11 +163,11 @@ export const EditSpaceProvider = ({ ); return ( - - - {children} - - + + {children} + ); }; diff --git a/x-pack/plugins/spaces/public/management/edit_space/provider/index.ts b/x-pack/plugins/spaces/public/management/edit_space/provider/index.ts index 7ae7301cd2c60..405f59c44a6f8 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/provider/index.ts +++ b/x-pack/plugins/spaces/public/management/edit_space/provider/index.ts @@ -5,9 +5,14 @@ * 2.0. */ -export { EditSpaceProvider, useEditSpaceServices, useEditSpaceStore } from './edit_space_provider'; +export { + EditSpaceProviderRoot, + EditSpaceProvider, + useEditSpaceServices, + useEditSpaceStore, +} from './edit_space_provider'; export type { - EditSpaceProviderProps, + EditSpaceProviderRootProps, EditSpaceServices, EditSpaceStore, } from './edit_space_provider'; diff --git a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx index 3595cefd1220c..7f99202e23791 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import crypto from 'crypto'; import React from 'react'; @@ -19,7 +19,7 @@ import { themeServiceMock, } from '@kbn/core/public/mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import type { Role } from '@kbn/security-plugin-types-common'; +import type { Role, SecurityLicense } from '@kbn/security-plugin-types-common'; import { createRawKibanaPrivileges, kibanaFeatures, @@ -33,11 +33,8 @@ import { FEATURE_PRIVILEGES_READ, } from '../../../../../common/constants'; import { spacesManagerMock } from '../../../../spaces_manager/spaces_manager.mock'; -import { - createPrivilegeAPIClientMock, - getPrivilegeAPIClientMock, -} from '../../../privilege_api_client.mock'; -import { createRolesAPIClientMock, getRolesAPIClientMock } from '../../../roles_api_client.mock'; +import { createPrivilegeAPIClientMock } from '../../../privilege_api_client.mock'; +import { createRolesAPIClientMock } from '../../../roles_api_client.mock'; import { EditSpaceProvider } from '../../provider'; const rolesAPIClient = createRolesAPIClientMock(); @@ -74,6 +71,9 @@ const spacesClientsInvocatorMock = jest.fn((fn) => const dispatchMock = jest.fn(); const onSaveCompleted = jest.fn(); const closeFlyout = jest.fn(); +const licenseMock = { + getFeatures: jest.fn(() => ({})), +} as unknown as SecurityLicense; const renderPrivilegeRolesForm = ({ preSelectedRoles, @@ -93,15 +93,20 @@ const renderPrivilegeRolesForm = ({ spacesManager, serverBasePath: '', getUrlForApp: jest.fn((_) => _), - getRolesAPIClient: getRolesAPIClientMock, - getPrivilegesAPIClient: getPrivilegeAPIClientMock, navigateToUrl: jest.fn(), + license: licenseMock, capabilities: { navLinks: {}, management: {}, catalogue: {}, spaces: { manage: true }, }, + dispatch: dispatchMock, + state: { + roles: new Map(), + fetchRolesError: false, + }, + invokeClient: spacesClientsInvocatorMock, }} > _), }} /> @@ -358,11 +360,11 @@ describe('PrivilegesRolesForm', () => { preSelectedRoles: roles, }); - await waitFor(() => null); - - expect(screen.getByTestId(`${FEATURE_PRIVILEGES_READ}-privilege-button`)).toHaveAttribute( - 'aria-pressed', - String(true) + await waitFor(() => + expect(screen.getByTestId(`${FEATURE_PRIVILEGES_READ}-privilege-button`)).toHaveAttribute( + 'aria-pressed', + String(true) + ) ); await user.click(screen.getByTestId('custom-privilege-button')); @@ -408,5 +410,116 @@ describe('PrivilegesRolesForm', () => { String(true) ); }); + + it('prevents customization up to sub privilege level by default', async () => { + const user = userEvent.setup(); + + const roles: Role[] = [ + createRole('test_role_1', [ + { base: [FEATURE_PRIVILEGES_READ], feature: {}, spaces: [space.id] }, + ]), + ]; + + getRolesSpy.mockResolvedValue([]); + getAllKibanaPrivilegeSpy.mockResolvedValue(createRawKibanaPrivileges(kibanaFeatures)); + + const featuresWithSubFeatures = kibanaFeatures.filter((kibanaFeature) => + Boolean(kibanaFeature.subFeatures.length) + ); + + renderPrivilegeRolesForm({ + preSelectedRoles: roles, + }); + + await user.click(screen.getByTestId('custom-privilege-button')); + + expect( + screen.getByTestId('space-assign-role-privilege-customization-form') + ).toBeInTheDocument(); + + const featureUT = featuresWithSubFeatures[0]; + + // change a single feature with sub features to read from default privilege "none" + await user.click(screen.getByTestId(`${featureUT.id}_${FEATURE_PRIVILEGES_READ}`)); + + // click on the accordion toggle to show sub features + await user.click( + screen.getByTestId( + `featurePrivilegeControls_${featureUT.category.id}_${featureUT.id}_accordionToggle` + ) + ); + + // sub feature table renders + expect( + screen.getByTestId(`${featureUT.category.id}_${featureUT.id}_subFeaturesTable`) + ).toBeInTheDocument(); + + // assert switch to customize sub feature can toggled + expect( + within( + screen.getByTestId( + `${featureUT.category.id}_${featureUT.id}_customizeSubFeaturesSwitchContainer` + ) + ).getByTestId('customizeSubFeaturePrivileges') + ).toBeDisabled(); + }); + + it('supports customization up to sub privilege level only when security license allows', async () => { + const user = userEvent.setup(); + + const roles: Role[] = [ + createRole('test_role_1', [ + { base: [FEATURE_PRIVILEGES_READ], feature: {}, spaces: [space.id] }, + ]), + ]; + + // enable sub feature privileges + (licenseMock.getFeatures as jest.Mock).mockReturnValue({ + allowSubFeaturePrivileges: true, + }); + + getRolesSpy.mockResolvedValue([]); + getAllKibanaPrivilegeSpy.mockResolvedValue(createRawKibanaPrivileges(kibanaFeatures)); + + const featuresWithSubFeatures = kibanaFeatures.filter((kibanaFeature) => + Boolean(kibanaFeature.subFeatures.length) + ); + + renderPrivilegeRolesForm({ + preSelectedRoles: roles, + }); + + await user.click(screen.getByTestId('custom-privilege-button')); + + expect( + screen.getByTestId('space-assign-role-privilege-customization-form') + ).toBeInTheDocument(); + + const featureUT = featuresWithSubFeatures[0]; + + // change a single feature with sub features to read from default privilege "none" + await user.click(screen.getByTestId(`${featureUT.id}_${FEATURE_PRIVILEGES_READ}`)); + + // click on the accordion toggle to show sub features + await user.click( + screen.getByTestId( + `featurePrivilegeControls_${featureUT.category.id}_${featureUT.id}_accordionToggle` + ) + ); + + // sub feature table renders + expect( + screen.getByTestId(`${featureUT.category.id}_${featureUT.id}_subFeaturesTable`) + ).toBeInTheDocument(); + + // assert switch to customize sub feature can toggled + expect( + within( + screen.getByTestId( + `${featureUT.category.id}_${featureUT.id}_customizeSubFeaturesSwitchContainer` + ) + ).getByTestId('customizeSubFeaturePrivileges') + ).not.toBeDisabled(); + }); }); }); diff --git a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx index f33c2cba25268..e0f3e8f3714c6 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/roles/component/space_assign_role_privilege_form.tsx @@ -46,7 +46,7 @@ import { FEATURE_PRIVILEGES_CUSTOM, FEATURE_PRIVILEGES_READ, } from '../../../../../common/constants'; -import { type EditSpaceServices, type EditSpaceStore, useEditSpaceServices } from '../../provider'; +import { useEditSpaceServices, useEditSpaceStore } from '../../provider'; type KibanaRolePrivilege = | keyof NonNullable @@ -62,9 +62,6 @@ interface PrivilegesRolesFormProps { * this is useful when the form is opened in edit mode */ defaultSelected?: Role[]; - storeDispatch: EditSpaceStore['dispatch']; - spacesClientsInvocator: EditSpaceServices['invokeClient']; - getUrlForApp: EditSpaceServices['getUrlForApp']; } const createRolesComboBoxOptions = (roles: Role[]): Array> => @@ -74,17 +71,9 @@ const createRolesComboBoxOptions = (roles: Role[]): Array = (props) => { - const { - space, - onSaveCompleted, - closeFlyout, - features, - defaultSelected = [], - spacesClientsInvocator, - storeDispatch, - getUrlForApp, - } = props; - const { logger, notifications } = useEditSpaceServices(); + const { space, onSaveCompleted, closeFlyout, features, defaultSelected = [] } = props; + const { logger, notifications, license, invokeClient, getUrlForApp } = useEditSpaceServices(); + const { dispatch: storeDispatch } = useEditSpaceStore(); const [assigningToRole, setAssigningToRole] = useState(false); const [fetchingDataDeps, setFetchingDataDeps] = useState(false); const [kibanaPrivileges, setKibanaPrivileges] = useState(null); @@ -98,7 +87,7 @@ export const PrivilegesRolesForm: FC = (props) => { async function fetchRequiredData(spaceId: string) { setFetchingDataDeps(true); - const [systemRoles, _kibanaPrivileges] = await spacesClientsInvocator((clients) => + const [systemRoles, _kibanaPrivileges] = await invokeClient((clients) => Promise.all([ clients.rolesClient.getRoles(), clients.privilegesClient.getAll({ includeActions: true, respectLicenseLevel: false }), @@ -123,7 +112,7 @@ export const PrivilegesRolesForm: FC = (props) => { } fetchRequiredData(space.id!).finally(() => setFetchingDataDeps(false)); - }, [space.id, spacesClientsInvocator]); + }, [invokeClient, space.id]); const selectedRolesCombinedPrivileges = useMemo(() => { const combinedPrivilege = new Set( @@ -315,7 +304,7 @@ export const PrivilegesRolesForm: FC = (props) => { return selectedRole.value!; }); - await spacesClientsInvocator((clients) => + await invokeClient((clients) => clients.rolesClient.bulkUpdateRoles({ rolesUpdate: updatedRoles }).then((response) => { setAssigningToRole(false); onSaveCompleted(response); @@ -338,13 +327,14 @@ export const PrivilegesRolesForm: FC = (props) => { }); } }, [ + roleSpacePrivilege, + roleCustomizationAnchor.value?.kibana, + roleCustomizationAnchor.privilegeIndex, selectedRoles, - spacesClientsInvocator, + invokeClient, storeDispatch, - onSaveCompleted, space.id, - roleSpacePrivilege, - roleCustomizationAnchor, + onSaveCompleted, logger, notifications.toasts, ]); @@ -571,7 +561,9 @@ export const PrivilegesRolesForm: FC = (props) => { ) } allSpacesSelected={false} - canCustomizeSubFeaturePrivileges={false} + canCustomizeSubFeaturePrivileges={ + license?.getFeatures().allowSubFeaturePrivileges ?? false + } /> )} diff --git a/x-pack/plugins/spaces/public/management/management_service.test.ts b/x-pack/plugins/spaces/public/management/management_service.test.ts index d1d7fe8d160a9..8a08334b0a76d 100644 --- a/x-pack/plugins/spaces/public/management/management_service.test.ts +++ b/x-pack/plugins/spaces/public/management/management_service.test.ts @@ -13,6 +13,7 @@ import { managementPluginMock } from '@kbn/management-plugin/public/mocks'; import { ManagementService } from './management_service'; import { getRolesAPIClientMock } from './roles_api_client.mock'; +import { getSecurityLicenseMock } from './security_license.mock'; import { EventTracker } from '../analytics'; import type { ConfigType } from '../config'; import type { PluginsStart } from '../plugin'; @@ -49,6 +50,7 @@ describe('ManagementService', () => { logger, getRolesAPIClient: getRolesAPIClientMock, getPrivilegesAPIClient: jest.fn(), + getSecurityLicense: getSecurityLicenseMock, eventTracker, isServerless: false, }); @@ -73,6 +75,7 @@ describe('ManagementService', () => { logger, getRolesAPIClient: getRolesAPIClientMock, getPrivilegesAPIClient: jest.fn(), + getSecurityLicense: getSecurityLicenseMock, eventTracker, isServerless: false, }); @@ -98,6 +101,7 @@ describe('ManagementService', () => { logger, getRolesAPIClient: jest.fn(), getPrivilegesAPIClient: jest.fn(), + getSecurityLicense: getSecurityLicenseMock, eventTracker, isServerless: false, }); diff --git a/x-pack/plugins/spaces/public/management/management_service.tsx b/x-pack/plugins/spaces/public/management/management_service.tsx index ba66229323bc8..317e091839534 100644 --- a/x-pack/plugins/spaces/public/management/management_service.tsx +++ b/x-pack/plugins/spaces/public/management/management_service.tsx @@ -5,30 +5,15 @@ * 2.0. */ -import type { StartServicesAccessor } from '@kbn/core/public'; -import type { Logger } from '@kbn/logging'; import type { ManagementApp, ManagementSetup } from '@kbn/management-plugin/public'; -import type { - PrivilegesAPIClientPublicContract, - RolesAPIClient, -} from '@kbn/security-plugin-types-public'; -import { spacesManagementApp } from './spaces_management_app'; -import type { EventTracker } from '../analytics'; -import type { ConfigType } from '../config'; -import type { PluginsStart } from '../plugin'; -import type { SpacesManager } from '../spaces_manager'; +import { + spacesManagementApp, + type CreateParams as SpacesManagementAppCreateParams, +} from './spaces_management_app'; -interface SetupDeps { +interface SetupDeps extends SpacesManagementAppCreateParams { management: ManagementSetup; - getStartServices: StartServicesAccessor; - spacesManager: SpacesManager; - config: ConfigType; - getRolesAPIClient: () => Promise; - eventTracker: EventTracker; - getPrivilegesAPIClient: () => Promise; - logger: Logger; - isServerless: boolean; } export class ManagementService { @@ -44,6 +29,7 @@ export class ManagementService { eventTracker, getPrivilegesAPIClient, isServerless, + getSecurityLicense, }: SetupDeps) { this.registeredSpacesManagementApp = management.sections.section.kibana.registerApp( spacesManagementApp.create({ @@ -55,6 +41,7 @@ export class ManagementService { eventTracker, getPrivilegesAPIClient, isServerless, + getSecurityLicense, }) ); } diff --git a/x-pack/plugins/spaces/public/management/security_license.mock.ts b/x-pack/plugins/spaces/public/management/security_license.mock.ts new file mode 100644 index 0000000000000..d5d6e73d03db4 --- /dev/null +++ b/x-pack/plugins/spaces/public/management/security_license.mock.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { BehaviorSubject, type Observable } from 'rxjs'; + +import type { SecurityLicense } from '@kbn/security-plugin-types-public'; + +type SecurityLicenseFeatures = SecurityLicense['features$'] extends Observable ? P : never; + +export const createSecurityLicenseMock = ({ + securityFeaturesConfig, +}: { + securityFeaturesConfig: SecurityLicenseFeatures; +}): SecurityLicense => { + return { + isLicenseAvailable: jest.fn(), + isEnabled: jest.fn(), + getFeatures: jest.fn(), + getUnavailableReason: jest.fn(), + hasAtLeast: jest.fn(), + getLicenseType: jest.fn(), + features$: new BehaviorSubject(securityFeaturesConfig), + }; +}; + +export const getSecurityLicenseMock = jest.fn().mockResolvedValue( + createSecurityLicenseMock({ + securityFeaturesConfig: { + showLinks: true, + showLogin: true, + allowLogin: true, + allowRbac: true, + allowFips: true, + showRoleMappingsManagement: true, + allowAccessAgreement: true, + allowAuditLogging: true, + allowSubFeaturePrivileges: true, + allowRoleFieldLevelSecurity: true, + allowRoleDocumentLevelSecurity: true, + allowRoleRemoteIndexPrivileges: true, + allowRemoteClusterPrivileges: true, + allowUserProfileCollaboration: true, + }, + }) +); diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx index ffafe432a5a3b..9c7056214d8fd 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.test.tsx @@ -77,6 +77,7 @@ async function mountApp(basePath: string, pathname: string, spaceId?: string) { logger, getRolesAPIClient: jest.fn(), getPrivilegesAPIClient: jest.fn(), + getSecurityLicense: jest.fn(), eventTracker, isServerless: false, }) @@ -102,6 +103,7 @@ describe('spacesManagementApp', () => { logger, getRolesAPIClient: jest.fn(), getPrivilegesAPIClient: jest.fn(), + getSecurityLicense: jest.fn(), eventTracker, isServerless: false, }) diff --git a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx index 7ad85d0ef7c52..a7111b72e563e 100644 --- a/x-pack/plugins/spaces/public/management/spaces_management_app.tsx +++ b/x-pack/plugins/spaces/public/management/spaces_management_app.tsx @@ -18,6 +18,7 @@ import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; import type { PrivilegesAPIClientPublicContract, RolesAPIClient, + SecurityLicense, } from '@kbn/security-plugin-types-public'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Route, Router, Routes } from '@kbn/shared-ux-router'; @@ -28,7 +29,7 @@ import type { ConfigType } from '../config'; import type { PluginsStart } from '../plugin'; import type { SpacesManager } from '../spaces_manager'; -interface CreateParams { +export interface CreateParams { getStartServices: StartServicesAccessor; spacesManager: SpacesManager; config: ConfigType; @@ -37,6 +38,7 @@ interface CreateParams { eventTracker: EventTracker; getPrivilegesAPIClient: () => Promise; isServerless: boolean; + getSecurityLicense: () => Promise; } export const spacesManagementApp = Object.freeze({ @@ -50,6 +52,7 @@ export const spacesManagementApp = Object.freeze({ getRolesAPIClient, getPrivilegesAPIClient, isServerless, + getSecurityLicense, }: CreateParams) { const title = i18n.translate('xpack.spaces.displayName', { defaultMessage: 'Spaces', @@ -149,6 +152,7 @@ export const spacesManagementApp = Object.freeze({ capabilities={application.capabilities} getUrlForApp={application.getUrlForApp} navigateToUrl={application.navigateToUrl} + getSecurityLicense={getSecurityLicense} serverBasePath={http.basePath.serverBasePath} getFeatures={features.getFeatures} http={http} diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index 8450aaff32657..107d3fab57652 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -10,7 +10,7 @@ import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kb import type { FeaturesPluginStart } from '@kbn/features-plugin/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import type { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public'; -import type { SecurityPluginStart } from '@kbn/security-plugin-types-public'; +import type { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin-types-public'; import { EventTracker, registerAnalyticsContext, registerSpacesEventTypes } from './analytics'; import type { ConfigType } from './config'; @@ -114,6 +114,18 @@ export class SpacesPlugin implements Plugin { + const { security } = await core.plugins.onSetup<{ security: SecurityPluginSetup }>( + 'security' + ); + + if (!security.found) { + throw new Error('Security plugin is not available as runtime dependency.'); + } + + return security.contract.license; + }; + if (plugins.home) { plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry()); } @@ -130,6 +142,7 @@ export class SpacesPlugin implements Plugin { const createWebHookConnector = () => createConnector(getWebHookAction()); // Failing: See https://github.com/elastic/kibana/issues/173804 - describe('@ess perform_bulk_action - ESS specific logic', () => { + // Failing: See https://github.com/elastic/kibana/issues/196470 + // Failing: See https://github.com/elastic/kibana/issues/196462 + describe.skip('@ess perform_bulk_action - ESS specific logic', () => { beforeEach(async () => { await deleteAllRules(supertest, log); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts index 6c41f4f916141..a7d32767f50ce 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine.ts @@ -7,26 +7,15 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../ftr_provider_context'; -import { EntityStoreUtils, elasticAssetCheckerFactory } from '../../utils'; +import { EntityStoreUtils } from '../../utils'; import { dataViewRouteHelpersFactory } from '../../utils/data_view'; export default ({ getService }: FtrProviderContext) => { const api = getService('securitySolutionApi'); const supertest = getService('supertest'); - const { - expectTransformExists, - expectTransformNotFound, - expectEnrichPolicyExists, - expectEnrichPolicyNotFound, - expectComponentTemplateExists, - expectComponentTemplateNotFound, - expectIngestPipelineExists, - expectIngestPipelineNotFound, - } = elasticAssetCheckerFactory(getService); const utils = EntityStoreUtils(getService); - - // TODO: unskip once permissions issue is resolved - describe.skip('@ess Entity Store Engine APIs', () => { + // Failing: See https://github.com/elastic/kibana/issues/196526 + describe.skip('@ess @skipInServerlessMKI Entity Store Engine APIs', () => { const dataView = dataViewRouteHelpersFactory(supertest); before(async () => { @@ -45,20 +34,12 @@ export default ({ getService }: FtrProviderContext) => { it('should have installed the expected user resources', async () => { await utils.initEntityEngineForEntityType('user'); - - await expectTransformExists('entities-v1-latest-ea_default_user_entity_store'); - await expectEnrichPolicyExists('entity_store_field_retention_user_default_v1'); - await expectComponentTemplateExists(`ea_default_user_entity_store-latest@platform`); - await expectIngestPipelineExists(`ea_default_user_entity_store-latest@platform`); + await utils.expectEngineAssetsExist('user'); }); it('should have installed the expected host resources', async () => { await utils.initEntityEngineForEntityType('host'); - - await expectTransformExists('entities-v1-latest-ea_default_host_entity_store'); - await expectEnrichPolicyExists('entity_store_field_retention_host_default_v1'); - await expectComponentTemplateExists(`ea_default_host_entity_store-latest@platform`); - await expectIngestPipelineExists(`ea_default_host_entity_store-latest@platform`); + await utils.expectEngineAssetsExist('host'); }); }); @@ -188,10 +169,7 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(200); - await expectTransformNotFound('entities-v1-latest-ea_default_host_entity_store'); - await expectEnrichPolicyNotFound('entity_store_field_retention_host_default_v1'); - await expectComponentTemplateNotFound(`ea_default_host_entity_store-latest@platform`); - await expectIngestPipelineNotFound(`ea_default_host_entity_store-latest@platform`); + await utils.expectEngineAssetsDoNotExist('host'); }); it('should delete the user entity engine', async () => { @@ -204,10 +182,7 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(200); - await expectTransformNotFound('entities-v1-latest-ea_default_user_entity_store'); - await expectEnrichPolicyNotFound('entity_store_field_retention_user_default_v1'); - await expectComponentTemplateNotFound(`ea_default_user_entity_store-latest@platform`); - await expectIngestPipelineNotFound(`ea_default_user_entity_store-latest@platform`); + await utils.expectEngineAssetsDoNotExist('user'); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts index ee86231fe23d4..de949730d3d10 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/engine_nondefault_spaces.ts @@ -9,15 +9,18 @@ import expect from '@kbn/expect'; import { v4 as uuidv4 } from 'uuid'; import { FtrProviderContextWithSpaces } from '../../../../ftr_provider_context_with_spaces'; import { EntityStoreUtils } from '../../utils'; +import { dataViewRouteHelpersFactory } from '../../utils/data_view'; + export default ({ getService }: FtrProviderContextWithSpaces) => { const api = getService('securitySolutionApi'); const spaces = getService('spaces'); const namespace = uuidv4().substring(0, 8); - + const supertest = getService('supertest'); const utils = EntityStoreUtils(getService, namespace); - // TODO: unskip once kibana system user has entity index privileges - describe.skip('@ess Entity Store Engine APIs in non-default space', () => { + describe('@ess Entity Store Engine APIs in non-default space', () => { + const dataView = dataViewRouteHelpersFactory(supertest, namespace); + before(async () => { await utils.cleanEngines(); await spaces.create({ @@ -25,9 +28,11 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { name: namespace, disabledFeatures: [], }); + await dataView.create('security-solution'); }); after(async () => { + await dataView.delete('security-solution'); await spaces.delete(namespace); }); @@ -38,18 +43,12 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { it('should have installed the expected user resources', async () => { await utils.initEntityEngineForEntityType('user'); - - const expectedTransforms = [`entities-v1-latest-ea_${namespace}_user_entity_store`]; - - await utils.expectTransformsExist(expectedTransforms); + await utils.expectEngineAssetsExist('user'); }); it('should have installed the expected host resources', async () => { await utils.initEntityEngineForEntityType('host'); - - const expectedTransforms = [`entities-v1-latest-ea_${namespace}_host_entity_store`]; - - await utils.expectTransformsExist(expectedTransforms); + await utils.expectEngineAssetsExist('host'); }); }); @@ -79,9 +78,9 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { expect(getResponse.body).to.eql({ status: 'started', type: 'host', - indexPattern: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', filter: '', + fieldHistoryLength: 10, + indexPattern: '', }); }); @@ -98,9 +97,9 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { expect(getResponse.body).to.eql({ status: 'started', type: 'user', - indexPattern: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', filter: '', + fieldHistoryLength: 10, + indexPattern: '', }); }); }); @@ -116,16 +115,16 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { { status: 'started', type: 'host', - indexPattern: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', filter: '', + fieldHistoryLength: 10, + indexPattern: '', }, { status: 'started', type: 'user', - indexPattern: - 'apm-*-transaction*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,traces-apm*,winlogbeat-*,-*elastic-cloud-logs-*', filter: '', + fieldHistoryLength: 10, + indexPattern: '', }, ]); }); @@ -200,10 +199,7 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { ) .expect(200); - await utils.expectTransformNotFound( - `entities-v1-history-ea_${namespace}_host_entity_store` - ); - await utils.expectTransformNotFound(`entities-v1-latest-ea_${namespace}_host_entity_store`); + await utils.expectEngineAssetsDoNotExist('host'); }); it('should delete the user entity engine', async () => { @@ -219,10 +215,7 @@ export default ({ getService }: FtrProviderContextWithSpaces) => { ) .expect(200); - await utils.expectTransformNotFound( - `entities-v1-history-ea_${namespace}_user_entity_store` - ); - await utils.expectTransformNotFound(`entities-v1-latest-ea_${namespace}_user_entity_store`); + await utils.expectEngineAssetsDoNotExist('user'); }); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entities_list.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entities_list.ts index 69f9c14d06086..9d7af16c79441 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entities_list.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entities_list.ts @@ -11,8 +11,7 @@ import { FtrProviderContext } from '../../../../ftr_provider_context'; export default ({ getService }: FtrProviderContext) => { const securitySolutionApi = getService('securitySolutionApi'); - // TODO: unskip once permissions issue is resolved - describe.skip('@ess Entity store - Entities list API', () => { + describe('@ess @skipInServerlessMKI Entity store - Entities list API', () => { describe('when the entity store is disable', () => { it("should return response with success status when the index doesn't exist", async () => { const { body } = await securitySolutionApi.listEntities({ diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/data_view.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/data_view.ts index 4eba56d3a757b..e94f7b7119ddf 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/data_view.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/data_view.ts @@ -12,7 +12,7 @@ export const dataViewRouteHelpersFactory = ( ) => ({ create: (name: string) => { return supertest - .post(`/api/data_views/data_view`) + .post(`/s/${namespace}/api/data_views/data_view`) .set('kbn-xsrf', 'foo') .send({ data_view: { @@ -26,13 +26,13 @@ export const dataViewRouteHelpersFactory = ( }, delete: (name: string) => { return supertest - .delete(`/api/data_views/data_view/${name}-${namespace}`) + .delete(`/s/${namespace}/api/data_views/data_view/${name}-${namespace}`) .set('kbn-xsrf', 'foo') .expect(200); }, updateIndexPattern: (name: string, indexPattern: string) => { return supertest - .post(`/api/data_views/data_view/${name}-${namespace}`) + .post(`/s/${namespace}/api/data_views/data_view/${name}-${namespace}`) .set('kbn-xsrf', 'foo') .send({ data_view: { diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts index 3ac171de1d4fd..24c1434b5e4a5 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/entity_store.ts @@ -6,16 +6,27 @@ */ import { EntityType } from '@kbn/security-solution-plugin/common/api/entity_analytics/entity_store/common.gen'; - +import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../api_integration/ftr_provider_context'; +import { elasticAssetCheckerFactory } from './elastic_asset_checker'; export const EntityStoreUtils = ( getService: FtrProviderContext['getService'], - namespace?: string + namespace: string = 'default' ) => { const api = getService('securitySolutionApi'); const es = getService('es'); const log = getService('log'); + const { + expectTransformExists, + expectTransformNotFound, + expectEnrichPolicyExists, + expectEnrichPolicyNotFound, + expectComponentTemplateExists, + expectComponentTemplateNotFound, + expectIngestPipelineExists, + expectIngestPipelineNotFound, + } = elasticAssetCheckerFactory(getService); log.debug(`EntityStoreUtils namespace: ${namespace}`); @@ -37,17 +48,24 @@ export const EntityStoreUtils = ( } }; - const initEntityEngineForEntityType = (entityType: EntityType) => { - log.info(`Initializing engine for entity type ${entityType} in namespace ${namespace}`); - return api - .initEntityEngine( - { - params: { entityType }, - body: {}, - }, - namespace - ) - .expect(200); + const initEntityEngineForEntityType = async (entityType: EntityType) => { + log.info( + `Initializing engine for entity type ${entityType} in namespace ${namespace || 'default'}` + ); + const res = await api.initEntityEngine( + { + params: { entityType }, + body: {}, + }, + namespace + ); + + if (res.status !== 200) { + log.error(`Failed to initialize engine for entity type ${entityType}`); + log.error(JSON.stringify(res.body)); + } + + expect(res.status).to.eql(200); }; const expectTransformStatus = async ( @@ -78,22 +96,25 @@ export const EntityStoreUtils = ( } }; - const expectTransformNotFound = async (transformId: string, attempts: number = 5) => { - return expectTransformStatus(transformId, false); - }; - const expectTransformExists = async (transformId: string) => { - return expectTransformStatus(transformId, true); + const expectEngineAssetsExist = async (entityType: EntityType) => { + await expectTransformExists(`entities-v1-latest-security_${entityType}_${namespace}`); + await expectEnrichPolicyExists(`entity_store_field_retention_${entityType}_${namespace}_v1`); + await expectComponentTemplateExists(`security_${entityType}_${namespace}-latest@platform`); + await expectIngestPipelineExists(`security_${entityType}_${namespace}-latest@platform`); }; - const expectTransformsExist = async (transformIds: string[]) => - Promise.all(transformIds.map((id) => expectTransformExists(id))); + const expectEngineAssetsDoNotExist = async (entityType: EntityType) => { + await expectTransformNotFound(`entities-v1-latest-security_${entityType}_${namespace}`); + await expectEnrichPolicyNotFound(`entity_store_field_retention_${entityType}_${namespace}_v1`); + await expectComponentTemplateNotFound(`security_${entityType}_${namespace}-latest@platform`); + await expectIngestPipelineNotFound(`security_${entityType}_${namespace}-latest@platform`); + }; return { cleanEngines, initEntityEngineForEntityType, expectTransformStatus, - expectTransformNotFound, - expectTransformExists, - expectTransformsExist, + expectEngineAssetsExist, + expectEngineAssetsDoNotExist, }; }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts index 8a636358c2649..027c0a20262a8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/investigation/saved_objects/trial_license_complete_tier/notes.ts @@ -15,7 +15,8 @@ export default function ({ getService }: FtrProviderContext) { const kibanaServer = getService('kibanaServer'); const supertest = getService('supertest'); - describe('Note - Saved Objects', () => { + // Failing: See https://github.com/elastic/kibana/issues/196492 + describe.skip('Note - Saved Objects', () => { const es = getService('es'); before(() => kibanaServer.savedObjects.cleanStandardList()); @@ -407,11 +408,7 @@ export default function ({ getService }: FtrProviderContext) { expect(notes[2].eventId).to.be('1'); }); - // TODO should add more tests for the filter query parameter (I don't know how it's supposed to work) - // TODO should add more tests for the MAX_UNASSOCIATED_NOTES advanced settings values - // TODO figure out why this test is failing on CI but not locally - // we can't really test for other users because the persistNote endpoint forces overrideOwner to be true then all the notes created here are owned by the elastic user it.skip('should retrieve all notes that have been created by a specific user', async () => { await Promise.all([ createNote(supertest, { text: 'first note' }), @@ -443,6 +440,116 @@ export default function ({ getService }: FtrProviderContext) { expect(totalCount).to.be(0); }); + + it('should retrieve all notes that have an association with a document only', async () => { + await Promise.all([ + createNote(supertest, { documentId: eventId1, text: 'associated with event-1 only' }), + createNote(supertest, { + savedObjectId: timelineId1, + text: 'associated with timeline-1 only', + }), + createNote(supertest, { + documentId: eventId1, + savedObjectId: timelineId1, + text: 'associated with event-1 and timeline-1', + }), + createNote(supertest, { text: 'associated with nothing' }), + ]); + + const response = await supertest + .get('/api/note?associatedFilter=document_only') + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31'); + + const { totalCount, notes } = response.body; + + expect(totalCount).to.be(1); + expect(notes[0].eventId).to.be(eventId1); + }); + + it('should retrieve all notes that have an association with a saved object only', async () => { + await Promise.all([ + createNote(supertest, { documentId: eventId1, text: 'associated with event-1 only' }), + createNote(supertest, { + savedObjectId: timelineId1, + text: 'associated with timeline-1 only', + }), + createNote(supertest, { + documentId: eventId1, + savedObjectId: timelineId1, + text: 'associated with event-1 and timeline-1', + }), + createNote(supertest, { text: 'associated with nothing' }), + ]); + + const response = await supertest + .get('/api/note?associatedFilter=saved_object_only') + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31'); + + const { totalCount, notes } = response.body; + + expect(totalCount).to.be(1); + expect(notes[0].timelineId).to.be(timelineId1); + }); + + it('should retrieve all notes that have an association with a document AND a saved object', async () => { + await Promise.all([ + createNote(supertest, { documentId: eventId1, text: 'associated with event-1 only' }), + createNote(supertest, { + savedObjectId: timelineId1, + text: 'associated with timeline-1 only', + }), + createNote(supertest, { + documentId: eventId1, + savedObjectId: timelineId1, + text: 'associated with event-1 and timeline-1', + }), + createNote(supertest, { text: 'associated with nothing' }), + ]); + + const response = await supertest + .get('/api/note?associatedFilter=document_and_saved_object') + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31'); + + const { totalCount, notes } = response.body; + + expect(totalCount).to.be(1); + expect(notes[0].eventId).to.be(eventId1); + expect(notes[0].timelineId).to.be(timelineId1); + }); + + it('should retrieve all notes that have an association with no document AND no saved object', async () => { + await Promise.all([ + createNote(supertest, { documentId: eventId1, text: 'associated with event-1 only' }), + createNote(supertest, { + savedObjectId: timelineId1, + text: 'associated with timeline-1 only', + }), + createNote(supertest, { + documentId: eventId1, + savedObjectId: timelineId1, + text: 'associated with event-1 and timeline-1', + }), + createNote(supertest, { text: 'associated with nothing' }), + ]); + + const response = await supertest + .get('/api/note?associatedFilter=orphan') + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31'); + + const { totalCount, notes } = response.body; + + expect(totalCount).to.be(1); + expect(notes[0].eventId).to.be(''); + expect(notes[0].timelineId).to.be(''); + }); + + // TODO should add more tests for the filter query parameter (I don't know how it's supposed to work) + // TODO should add more tests for the MAX_UNASSOCIATED_NOTES advanced settings values + // TODO add more tests to check the combination of filters (user, association and filter) }); }); } diff --git a/yarn.lock b/yarn.lock index ec4c8f0e0837f..ed8af28c675f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1844,9 +1844,9 @@ "@elastic/search-ui" "1.20.2" "@elastic/request-converter@^8.15.4": - version "8.15.4" - resolved "https://registry.yarnpkg.com/@elastic/request-converter/-/request-converter-8.15.4.tgz#332dc6266841b5a578c92e1655eee46b82c47ed2" - integrity sha512-iZDQpZpygV+AVOweaDzTsMJBfa2hwwduPXNNzk/yTXgC9qtjmns/AjehtLStKXs274+u3fg+BFxVt6NcMwUAAg== + version "8.16.0" + resolved "https://registry.yarnpkg.com/@elastic/request-converter/-/request-converter-8.16.0.tgz#e607d06d898ec290c7a9412104d7fa67d5fb9c8c" + integrity sha512-tSwCJMoX3/hme1HXi7ewfP5E+BLFV2LcItt3EB4eucWPKEtG3SqJ3iuK3ygWm1PodPz8Mww/DMaxTJFERb/usg== dependencies: child-process-promise "^2.2.1" commander "^12.1.0" @@ -21081,7 +21081,7 @@ isbinaryfile@4.0.2: isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isexe@^3.1.1: version "3.1.1" @@ -26411,7 +26411,7 @@ prr@~1.0.1: pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== psl@^1.1.33: version "1.4.0" @@ -29690,7 +29690,7 @@ string-replace-loader@^2.2.0: loader-utils "^1.2.3" schema-utils "^1.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -29708,6 +29708,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -29818,7 +29827,7 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -29832,6 +29841,13 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -31140,9 +31156,9 @@ uc.micro@^2.0.0, uc.micro@^2.1.0: integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== uglify-js@^3.1.4: - version "3.17.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" - integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + version "3.19.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== unbox-primitive@^1.0.2: version "1.0.2" @@ -32733,7 +32749,7 @@ word-wrap@~1.2.3: wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== worker-farm@^1.7.0: version "1.7.0" @@ -32754,7 +32770,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -32780,6 +32796,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -32892,7 +32917,7 @@ xpath@^0.0.33: resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.33.tgz#5136b6094227c5df92002e7c3a13516a5074eb07" integrity sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA== -"xstate5@npm:xstate@^5.18.1", xstate@^5.18.1: +"xstate5@npm:xstate@^5.18.1": version "5.18.1" resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.18.1.tgz#c4d43ceaba6e6c31705d36bd96e285de4be4f7f4" integrity sha512-m02IqcCQbaE/kBQLunwub/5i8epvkD2mFutnL17Oeg1eXTShe1sRF4D5mhv1dlaFO4vbW5gRGRhraeAD5c938g== @@ -32902,6 +32927,11 @@ xstate@^4.38.2: resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.38.2.tgz#1b74544fc9c8c6c713ba77f81c6017e65aa89804" integrity sha512-Fba/DwEPDLneHT3tbJ9F3zafbQXszOlyCJyQqqdzmtlY/cwE2th462KK48yaANf98jHlP6lJvxfNtN0LFKXPQg== +xstate@^5.18.1: + version "5.18.1" + resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.18.1.tgz#c4d43ceaba6e6c31705d36bd96e285de4be4f7f4" + integrity sha512-m02IqcCQbaE/kBQLunwub/5i8epvkD2mFutnL17Oeg1eXTShe1sRF4D5mhv1dlaFO4vbW5gRGRhraeAD5c938g== + "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -32937,7 +32967,7 @@ y18n@^5.0.5: yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== yallist@^3.0.2: version "3.1.1"