diff --git a/.buildkite/scripts/steps/checks/quick_checks.txt b/.buildkite/scripts/steps/checks/quick_checks.txt index 5bbebeac65aef..4c806e2cbf39d 100644 --- a/.buildkite/scripts/steps/checks/quick_checks.txt +++ b/.buildkite/scripts/steps/checks/quick_checks.txt @@ -2,7 +2,6 @@ .buildkite/scripts/steps/checks/packages.sh .buildkite/scripts/steps/checks/bazel_packages.sh .buildkite/scripts/steps/checks/verify_notice.sh -.buildkite/scripts/steps/checks/plugin_list_docs.sh .buildkite/scripts/steps/checks/event_log.sh .buildkite/scripts/steps/checks/telemetry.sh .buildkite/scripts/steps/checks/jest_configs.sh diff --git a/.buildkite/scripts/steps/console_definitions_sync.sh b/.buildkite/scripts/steps/console_definitions_sync.sh index 8431d8debc59f..04de50115b049 100755 --- a/.buildkite/scripts/steps/console_definitions_sync.sh +++ b/.buildkite/scripts/steps/console_definitions_sync.sh @@ -40,7 +40,7 @@ main () { git config --global user.name "$KIBANA_MACHINE_USERNAME" git config --global user.email '42973632+kibanamachine@users.noreply.github.com' - PR_TITLE="[Console] Update console definitions (${branch_name})" + PR_TITLE="[Console] Update console definitions (${BUILDKITE_BRANCH})" PR_BODY='This PR updates the console definitions to match the latest ones from the @elastic/elasticsearch-specification repo.' # Check if a PR already exists diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e64dfcd5b05dc..67f6e74e71f76 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -392,7 +392,6 @@ src/platform/packages/private/kbn-transpose-utils @elastic/kibana-visualizations src/platform/packages/private/kbn-ui-shared-deps-npm @elastic/kibana-operations src/platform/packages/private/kbn-ui-shared-deps-src @elastic/kibana-operations src/platform/packages/private/kbn-unsaved-changes-badge @elastic/kibana-data-discovery -src/platform/packages/private/serverless/project_switcher @elastic/appex-sharedux src/platform/packages/private/serverless/settings/common @elastic/appex-sharedux @elastic/kibana-management src/platform/packages/private/serverless/types @elastic/appex-sharedux src/platform/packages/private/shared-ux/page/analytics_no_data/impl @elastic/appex-sharedux @@ -800,6 +799,7 @@ x-pack/platform/packages/private/security/ui_components @elastic/kibana-security x-pack/platform/packages/shared/ai-assistant/common @elastic/search-kibana x-pack/platform/packages/shared/ai-assistant/icon @elastic/appex-sharedux x-pack/platform/packages/shared/ai-infra/inference-common @elastic/appex-ai-infra +x-pack/platform/packages/shared/ai-infra/inference-langchain @elastic/appex-ai-infra x-pack/platform/packages/shared/ai-infra/product-doc-common @elastic/appex-ai-infra x-pack/platform/packages/shared/file-upload-common @elastic/ml-ui x-pack/platform/packages/shared/index-lifecycle-management/index_lifecycle_management_common_shared @elastic/kibana-management @@ -2216,6 +2216,7 @@ x-pack/test/security_solution_api_integration/test_suites/sources @elastic/secur /x-pack/test/security_solution_cypress/* @elastic/security-engineering-productivity /x-pack/test/security_solution_cypress/cypress/* @elastic/security-engineering-productivity /x-pack/test/security_solution_cypress/cypress/tasks/login.ts @elastic/security-engineering-productivity +/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/common.ts @elastic/security-engineering-productivity /x-pack/test/security_solution_cypress/es_archives @elastic/security-engineering-productivity /x-pack/test/security_solution_playwright @elastic/security-engineering-productivity /x-pack/solutions/security/plugins/security_solution/scripts/run_cypress @MadameSheema @patrykkopycinski @maximpn @banderror diff --git a/api_docs/kbn_streams_schema.devdocs.json b/api_docs/kbn_streams_schema.devdocs.json index fd75b8c2c647a..19a33601902c3 100644 --- a/api_docs/kbn_streams_schema.devdocs.json +++ b/api_docs/kbn_streams_schema.devdocs.json @@ -1593,10 +1593,10 @@ }, { "parentPluginId": "@kbn/streams-schema", - "id": "def-common.isUnWiredStreamGetResponse", + "id": "def-common.isUnwiredStreamGetResponse", "type": "Function", "tags": [], - "label": "isUnWiredStreamGetResponse", + "label": "isUnwiredStreamGetResponse", "description": [], "signature": [ "> tasks in one request. Additionally, reindexing tasks started or resumed -via the batch endpoint will be placed on a queue and executed one-by-one, which ensures that minimal cluster resources -are consumed over time. +Start or resume upgrading multiple indices in one request. Additionally, <> tasks for upgrading +indices that are started or resumed via the batch endpoint will be placed on a queue and executed one-by-one. This ensures that +minimal cluster resources are consumed over time. + +NOTE: This API does not support data streams. [[batch-start-resume-reindex-request]] ==== Request diff --git a/fleet_packages.json b/fleet_packages.json index f0dcf1efd0066..ed8a6d527b032 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -34,7 +34,7 @@ }, { "name": "endpoint", - "version": "8.15.1" + "version": "8.18.0" }, { "name": "fleet_server", diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index f89ad4a5c938d..0f6ece5378a4a 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -26835,6 +26835,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -26855,6 +26863,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -26968,6 +26995,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -27925,6 +27955,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -27945,6 +27983,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -28058,6 +28115,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -28953,6 +29013,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -28973,6 +29041,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -29086,6 +29173,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -30131,6 +30221,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -30151,6 +30249,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -30264,6 +30381,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -31206,6 +31326,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -31226,6 +31354,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -31339,6 +31486,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -32219,6 +32369,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -32239,6 +32397,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -32352,6 +32529,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index b9d0b75240c02..6689dc845ca3b 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -26835,6 +26835,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -26855,6 +26863,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -26968,6 +26995,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -27925,6 +27955,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -27945,6 +27983,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -28058,6 +28115,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -28953,6 +29013,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -28973,6 +29041,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -29086,6 +29173,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -30131,6 +30221,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -30151,6 +30249,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -30264,6 +30381,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -31206,6 +31326,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -31226,6 +31354,25 @@ "secrets": { "additionalProperties": false, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -31339,6 +31486,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" @@ -32219,6 +32369,14 @@ "is_preconfigured": { "type": "boolean" }, + "kibana_api_key": { + "nullable": true, + "type": "string" + }, + "kibana_url": { + "nullable": true, + "type": "string" + }, "name": { "type": "string" }, @@ -32239,6 +32397,25 @@ "secrets": { "additionalProperties": true, "properties": { + "kibana_api_key": { + "anyOf": [ + { + "additionalProperties": true, + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + { + "type": "string" + } + ] + }, "service_token": { "anyOf": [ { @@ -32352,6 +32529,9 @@ }, "type": "object" }, + "sync_integrations": { + "type": "boolean" + }, "type": { "enum": [ "remote_elasticsearch" diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index d0759a6daad16..682fc872612e3 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -8011,6 +8011,42 @@ paths: '200': content: application/json: + examples: + success: + value: + application: {} + cluster: + all: true + manage: true + manage_api_key: true + manage_index_templates: true + manage_ml: true + manage_own_api_key: true + manage_pipeline: true + manage_security: true + manage_transform: true + monitor: true + monitor_ml: true + monitor_transform: true + has_all_requested: true + has_encryption_key: true + index: + .alerts-security.alerts-default: + all: true + create: true + create_doc: true + create_index: true + delete: true + delete_index: true + index: true + maintenance: true + manage: true + monitor: true + read: true + view_index_metadata: true + write: true + is_authenticated: true + username: elastic schema: type: object properties: @@ -8682,6 +8718,23 @@ paths: requestBody: content: application/json: + examples: + add: + value: + assignees: + add: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + remove: [] + ids: + - 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 + remove: + value: + assignees: + add: [] + remove: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + ids: + - 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 schema: type: object properties: @@ -8690,13 +8743,32 @@ paths: description: Details about the assignees to assign and unassign. ids: $ref: '#/components/schemas/Security_Detections_API_AlertIds' - description: List of alerts ids to assign and unassign passed assignees. required: - assignees - ids required: true responses: '200': + content: + application/ndjson: + examples: + add: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + - bulk: 0, + - search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 76, + total: 1, + updated: 1, + version_conflicts: 0, description: Indicates a successful call. '400': description: Invalid request. @@ -8711,6 +8783,35 @@ paths: requestBody: content: application/json: + examples: + query: + value: + aggs: + alertsByGrouping: + terms: + field: host.name + size: 10 + missingFields: + missing: + field: host.name + query: + bool: + filter: + - bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + - range: + '@timestamp': + gte: '2025-01-17T08:00:00.000Z' + lte: '2025-01-18T07:59:59.999Z' + runtime_mappings: {} + size: 0 schema: description: Elasticsearch query and aggregation request type: object @@ -8748,6 +8849,31 @@ paths: '200': content: application/json: + examples: + success: + value: + _shards: + failed: 0 + skipped: 0 + successful: 1 + total: 1 + aggregations: + alertsByGrouping: + buckets: + - doc_count: 5 + key: Host-f43kkddfyc + doc_count_error_upper_bound: 0 + sum_other_doc_count: 0 + missingFields: + doc_count: 0 + hits: + hits: [] + max_score: null + total: + relation: eq + value: 5 + timed_out: false + took: 0 schema: additionalProperties: true description: Elasticsearch search response @@ -8784,6 +8910,43 @@ paths: requestBody: content: application/json: + examples: + byId: + value: + signal_ids: + - 80e1383f856e67c1b7f7a1634744fa6d66b6e2ef7aa26d226e57afb5a7b2b4a1 + status: closed + byQuery: + value: + conflicts: proceed + query: + bool: + filter: + - '@timestamp': + format: strict_date_optional_time + gte: '2024-10-23T07:00:00.000Z' + lte: '2025-01-21T20:12:11.704Z' + range: null + - bool: + filter: + bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + - '@timestamp': + format: strict_date_optional_time + gte: '2024-10-23T07:00:00.000Z' + lte: '2025-01-21T20:12:11.704Z' + range: null + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + must: [] + must_not: [] + should: [] + status: closed schema: oneOf: - $ref: '#/components/schemas/Security_Detections_API_SetAlertsStatusByIds' @@ -8794,6 +8957,41 @@ paths: '200': content: application/json: + examples: + byId: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 81 + total: 1 + updated: 1 + version_conflicts: 0 + byQuery: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 100 + total: 17 + updated: 17 + version_conflicts: 0 schema: additionalProperties: true description: Elasticsearch update by query response @@ -8833,6 +9031,23 @@ paths: requestBody: content: application/json: + examples: + add: + value: + ids: + - 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: + - Duplicate + tags_to_remove: [] + remove: + value: + ids: + - 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: [] + tags_to_remove: + - Duplicate schema: type: object properties: @@ -8849,6 +9064,24 @@ paths: '200': content: application/json: + examples: + success: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + bulk: 0, + search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 68, + total: 1, + updated: 1, + version_conflicts: 0, schema: additionalProperties: true description: Elasticsearch update by query response @@ -24805,6 +25038,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -24822,6 +25061,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -24901,6 +25150,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -25532,6 +25783,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -25549,6 +25806,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -25628,6 +25895,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -26221,6 +26490,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -26238,6 +26513,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -26317,6 +26602,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -27006,6 +27293,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -27023,6 +27316,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -27102,6 +27405,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -27720,6 +28025,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -27737,6 +28048,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -27816,6 +28137,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -28396,6 +28719,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -28413,6 +28742,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -28492,6 +28831,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -44817,22 +45158,28 @@ components: type: object properties: add: - description: A list of users ids to assign. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + description: A list of users ids to assign. + format: nonempty + minLength: 1 + type: string type: array remove: - description: A list of users ids to unassign. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + description: A list of users ids to unassign. + format: nonempty + minLength: 1 + type: string type: array required: - add - remove Security_Detections_API_AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array Security_Detections_API_AlertsIndex: @@ -44854,6 +45201,7 @@ components: - additionalProperties: true type: object Security_Detections_API_AlertStatus: + description: The status of an alert, which can be `open`, `acknowledged`, `in-progress`, or `closed`. enum: - open - closed @@ -44904,8 +45252,12 @@ components: - suppress type: string Security_Detections_API_AlertTag: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + description: Use alert tags to organize related alerts into categories that you can filter and group. + format: nonempty + minLength: 1 + type: string Security_Detections_API_AlertTags: + description: List of keywords to organize related alerts into categories that you can filter and group. items: $ref: '#/components/schemas/Security_Detections_API_AlertTag' type: array @@ -48911,8 +49263,11 @@ components: type: object properties: signal_ids: + description: List of alert `id`s. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array status: @@ -48938,6 +49293,7 @@ components: - query - status Security_Detections_API_SetAlertTags: + description: Object with list of tags to add and remove. type: object properties: tags_to_add: @@ -50694,9 +51050,11 @@ components: - microsoft_defender_endpoint type: string Security_Endpoint_Management_API_AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array Security_Endpoint_Management_API_CaseIds: @@ -50848,7 +51206,22 @@ components: required: - parameters Security_Endpoint_Management_API_GetProcessesRouteRequestBody: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' + alert_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' + case_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' + comment: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' + endpoint_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' + parameters: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' + required: + - endpoint_ids Security_Endpoint_Management_API_HostPathScriptParameters: type: object properties: @@ -50867,7 +51240,22 @@ components: required: - hostPath Security_Endpoint_Management_API_IsolateRouteRequestBody: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' + alert_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' + case_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' + comment: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' + endpoint_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' + parameters: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' + required: + - endpoint_ids Security_Endpoint_Management_API_KillProcessRouteRequestBody: allOf: - type: object @@ -50947,33 +51335,6 @@ components: type: string required: - hostStatuses - Security_Endpoint_Management_API_NonEmptyString: - description: A string that does not contain only whitespace characters - format: nonempty - minLength: 1 - type: string - Security_Endpoint_Management_API_NoParametersRequestSchema: - type: object - properties: - body: - type: object - properties: - agent_type: - $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' - alert_ids: - $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' - case_ids: - $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' - comment: - $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' - endpoint_ids: - $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' - parameters: - $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' - required: - - endpoint_ids - required: - - body Security_Endpoint_Management_API_Page: default: 1 description: Page number @@ -51126,7 +51487,22 @@ components: minLength: 1 type: array Security_Endpoint_Management_API_UnisolateRouteRequestBody: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' + alert_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' + case_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' + comment: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' + endpoint_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' + parameters: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' + required: + - endpoint_ids Security_Endpoint_Management_API_UploadRouteRequestBody: allOf: - type: object diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index e355846c365c6..f1df20e1821f0 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -9498,6 +9498,11 @@ paths: '200': content: application/json: + examples: + success: + value: + index_mapping_outdated: false + name: .alerts-security.alerts-default schema: type: object properties: @@ -9590,6 +9595,42 @@ paths: '200': content: application/json: + examples: + success: + value: + application: {} + cluster: + all: true + manage: true + manage_api_key: true + manage_index_templates: true + manage_ml: true + manage_own_api_key: true + manage_pipeline: true + manage_security: true + manage_transform: true + monitor: true + monitor_ml: true + monitor_transform: true + has_all_requested: true + has_encryption_key: true + index: + .alerts-security.alerts-default: + all: true + create: true + create_doc: true + create_index: true + delete: true + delete_index: true + index: true + maintenance: true + manage: true + monitor: true + read: true + view_index_metadata: true + write: true + is_authenticated: true + username: elastic schema: type: object properties: @@ -10509,6 +10550,23 @@ paths: requestBody: content: application/json: + examples: + add: + value: + assignees: + add: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + remove: [] + ids: + - 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 + remove: + value: + assignees: + add: [] + remove: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + ids: + - 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 schema: type: object properties: @@ -10517,13 +10575,32 @@ paths: description: Details about the assignees to assign and unassign. ids: $ref: '#/components/schemas/Security_Detections_API_AlertIds' - description: List of alerts ids to assign and unassign passed assignees. required: - assignees - ids required: true responses: '200': + content: + application/ndjson: + examples: + add: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + - bulk: 0, + - search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 76, + total: 1, + updated: 1, + version_conflicts: 0, description: Indicates a successful call. '400': description: Invalid request. @@ -10542,9 +10619,13 @@ paths: content: application/json: schema: + example: + migration_ids: + - 924f7c50-505f-11eb-ae0a-3fa2e626a51d type: object properties: migration_ids: + description: Array of `migration_id`s to finalize. items: type: string minItems: 1 @@ -10557,6 +10638,17 @@ paths: '200': content: application/json: + examples: + success: + value: + migrations: + - completed: true + destinationIndex: .siem-signals-default-000002-r000016 + id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + sourceIndex: .siem-signals-default-000002 + status: success + updated: '2021-01-06T22:05:56.859Z' + version: 16 schema: items: $ref: '#/components/schemas/Security_Detections_API_MigrationFinalizationResult' @@ -10601,9 +10693,13 @@ paths: content: application/json: schema: + example: + migration_ids: + - 924f7c50-505f-11eb-ae0a-3fa2e626a51d type: object properties: migration_ids: + description: Array of `migration_id`s to cleanup. items: type: string minItems: 1 @@ -10616,6 +10712,16 @@ paths: '200': content: application/json: + examples: + success: + value: + migrations: + - destinationIndex: .siem-signals-default-000002-r000016 + id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + sourceIndex: .siem-signals-default-000002 + status: success + updated: '2021-01-06T22:05:56.859Z' + version: 16 schema: items: $ref: '#/components/schemas/Security_Detections_API_MigrationCleanupResult' @@ -10653,13 +10759,21 @@ paths: requestBody: content: application/json: + examples: + singleIndex: + value: + index: + - .siem-signals-default-000001 schema: allOf: - type: object properties: index: + description: Array of index names to migrate. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array required: @@ -10671,6 +10785,13 @@ paths: '200': content: application/json: + examples: + success: + value: + indices: + - index: .siem-signals-default-000001, + migration_id: 923f7c50-505f-11eb-ae0a-3fa2e626a51d + migration_index: .siem-signals-default-000001-r000016 schema: type: object properties: @@ -10708,7 +10829,7 @@ paths: tags: - Security Detections API /api/detection_engine/signals/migration_status: - post: + get: deprecated: true description: Retrieve indices that contain detection alerts of a particular age, along with migration information for each of those indices. operationId: ReadAlertsMigrationStatus @@ -10721,12 +10842,37 @@ paths: description: | Time from which data is analyzed. For example, now-4200s means the rule analyzes data from 70 minutes before its start time. Defaults to now-6m (analyzes data from 6 minutes before the start time). + example: now-30d format: date-math type: string responses: '200': content: application/json: + examples: + success: + value: + indices: + - index: .siem-signals-default-000002 + is_outdated: true + migrations: + - id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + status: pending + updated: '2021-01-06T20:41:37.173Z' + version: 16 + signal_versions: + - count: 100 + version: 15 + - count: 87 + version: 16 + version: 15 + - index: .siem-signals-default-000003 + is_outdated: false + migrations: [] + signal_versions: + - count: 54 + version: 16 + version: 16 schema: type: object properties: @@ -10767,6 +10913,35 @@ paths: requestBody: content: application/json: + examples: + query: + value: + aggs: + alertsByGrouping: + terms: + field: host.name + size: 10 + missingFields: + missing: + field: host.name + query: + bool: + filter: + - bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + - range: + '@timestamp': + gte: '2025-01-17T08:00:00.000Z' + lte: '2025-01-18T07:59:59.999Z' + runtime_mappings: {} + size: 0 schema: description: Elasticsearch query and aggregation request type: object @@ -10804,6 +10979,31 @@ paths: '200': content: application/json: + examples: + success: + value: + _shards: + failed: 0 + skipped: 0 + successful: 1 + total: 1 + aggregations: + alertsByGrouping: + buckets: + - doc_count: 5 + key: Host-f43kkddfyc + doc_count_error_upper_bound: 0 + sum_other_doc_count: 0 + missingFields: + doc_count: 0 + hits: + hits: [] + max_score: null + total: + relation: eq + value: 5 + timed_out: false + took: 0 schema: additionalProperties: true description: Elasticsearch search response @@ -10839,6 +11039,43 @@ paths: requestBody: content: application/json: + examples: + byId: + value: + signal_ids: + - 80e1383f856e67c1b7f7a1634744fa6d66b6e2ef7aa26d226e57afb5a7b2b4a1 + status: closed + byQuery: + value: + conflicts: proceed + query: + bool: + filter: + - '@timestamp': + format: strict_date_optional_time + gte: '2024-10-23T07:00:00.000Z' + lte: '2025-01-21T20:12:11.704Z' + range: null + - bool: + filter: + bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + - '@timestamp': + format: strict_date_optional_time + gte: '2024-10-23T07:00:00.000Z' + lte: '2025-01-21T20:12:11.704Z' + range: null + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + must: [] + must_not: [] + should: [] + status: closed schema: oneOf: - $ref: '#/components/schemas/Security_Detections_API_SetAlertsStatusByIds' @@ -10849,6 +11086,41 @@ paths: '200': content: application/json: + examples: + byId: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 81 + total: 1 + updated: 1 + version_conflicts: 0 + byQuery: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 100 + total: 17 + updated: 17 + version_conflicts: 0 schema: additionalProperties: true description: Elasticsearch update by query response @@ -10887,6 +11159,23 @@ paths: requestBody: content: application/json: + examples: + add: + value: + ids: + - 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: + - Duplicate + tags_to_remove: [] + remove: + value: + ids: + - 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: [] + tags_to_remove: + - Duplicate schema: type: object properties: @@ -10903,6 +11192,24 @@ paths: '200': content: application/json: + examples: + success: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + bulk: 0, + search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 68, + total: 1, + updated: 1, + version_conflicts: 0, schema: additionalProperties: true description: Elasticsearch update by query response @@ -26794,6 +27101,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -26811,6 +27124,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -26890,6 +27213,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -27520,6 +27845,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -27537,6 +27868,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -27616,6 +27957,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -28209,6 +28552,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -28226,6 +28575,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -28305,6 +28664,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -28992,6 +29353,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -29009,6 +29376,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -29088,6 +29465,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -29705,6 +30084,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -29722,6 +30107,16 @@ paths: additionalProperties: false type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: false + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: false @@ -29801,6 +30196,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -30381,6 +30778,12 @@ paths: type: boolean is_preconfigured: type: boolean + kibana_api_key: + nullable: true + type: string + kibana_url: + nullable: true + type: string name: type: string preset: @@ -30398,6 +30801,16 @@ paths: additionalProperties: true type: object properties: + kibana_api_key: + anyOf: + - additionalProperties: true + type: object + properties: + id: + type: string + required: + - id + - type: string service_token: anyOf: - additionalProperties: true @@ -30477,6 +30890,8 @@ paths: - certificate - strict type: string + sync_integrations: + type: boolean type: enum: - remote_elasticsearch @@ -51320,22 +51735,28 @@ components: type: object properties: add: - description: A list of users ids to assign. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + description: A list of users ids to assign. + format: nonempty + minLength: 1 + type: string type: array remove: - description: A list of users ids to unassign. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + description: A list of users ids to unassign. + format: nonempty + minLength: 1 + type: string type: array required: - add - remove Security_Detections_API_AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array Security_Detections_API_AlertsIndex: @@ -51380,12 +51801,15 @@ components: type: object properties: requests_per_second: + description: The throttle for the migration task in sub-requests per second. Corresponds to requests_per_second on the Reindex API. minimum: 1 type: integer size: + description: Number of alerts to migrate per batch. Corresponds to the source.size option on the Reindex API. minimum: 1 type: integer slices: + description: The number of subtasks for the migration task. Corresponds to slices on the Reindex API. minimum: 1 type: integer Security_Detections_API_AlertsSort: @@ -51400,6 +51824,7 @@ components: - additionalProperties: true type: object Security_Detections_API_AlertStatus: + description: The status of an alert, which can be `open`, `acknowledged`, `in-progress`, or `closed`. enum: - open - closed @@ -51450,8 +51875,12 @@ components: - suppress type: string Security_Detections_API_AlertTag: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + description: Use alert tags to organize related alerts into categories that you can filter and group. + format: nonempty + minLength: 1 + type: string Security_Detections_API_AlertTags: + description: List of keywords to organize related alerts into categories that you can filter and group. items: $ref: '#/components/schemas/Security_Detections_API_AlertTag' type: array @@ -55594,8 +56023,11 @@ components: type: object properties: signal_ids: + description: List of alert `id`s. items: - $ref: '#/components/schemas/Security_Detections_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array status: @@ -55621,6 +56053,7 @@ components: - query - status Security_Detections_API_SetAlertTags: + description: Object with list of tags to add and remove. type: object properties: tags_to_add: @@ -57384,9 +57817,11 @@ components: - microsoft_defender_endpoint type: string Security_Endpoint_Management_API_AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array Security_Endpoint_Management_API_CaseIds: @@ -57538,7 +57973,22 @@ components: required: - parameters Security_Endpoint_Management_API_GetProcessesRouteRequestBody: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' + alert_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' + case_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' + comment: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' + endpoint_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' + parameters: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' + required: + - endpoint_ids Security_Endpoint_Management_API_HostPathScriptParameters: type: object properties: @@ -57557,7 +58007,22 @@ components: required: - hostPath Security_Endpoint_Management_API_IsolateRouteRequestBody: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' + alert_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' + case_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' + comment: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' + endpoint_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' + parameters: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' + required: + - endpoint_ids Security_Endpoint_Management_API_KillProcessRouteRequestBody: allOf: - type: object @@ -57637,33 +58102,6 @@ components: type: string required: - hostStatuses - Security_Endpoint_Management_API_NonEmptyString: - description: A string that does not contain only whitespace characters - format: nonempty - minLength: 1 - type: string - Security_Endpoint_Management_API_NoParametersRequestSchema: - type: object - properties: - body: - type: object - properties: - agent_type: - $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' - alert_ids: - $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' - case_ids: - $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' - comment: - $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' - endpoint_ids: - $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' - parameters: - $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' - required: - - endpoint_ids - required: - - body Security_Endpoint_Management_API_Page: default: 1 description: Page number @@ -57816,7 +58254,22 @@ components: minLength: 1 type: array Security_Endpoint_Management_API_UnisolateRouteRequestBody: - $ref: '#/components/schemas/Security_Endpoint_Management_API_NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AgentTypes' + alert_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_AlertIds' + case_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_CaseIds' + comment: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Comment' + endpoint_ids: + $ref: '#/components/schemas/Security_Endpoint_Management_API_EndpointIds' + parameters: + $ref: '#/components/schemas/Security_Endpoint_Management_API_Parameters' + required: + - endpoint_ids Security_Endpoint_Management_API_UploadRouteRequestBody: allOf: - type: object diff --git a/package.json b/package.json index 17a826fdb7a39..85355a4488a03 100644 --- a/package.json +++ b/package.json @@ -580,6 +580,7 @@ "@kbn/inference-common": "link:x-pack/platform/packages/shared/ai-infra/inference-common", "@kbn/inference-endpoint-plugin": "link:x-pack/platform/plugins/shared/inference_endpoint", "@kbn/inference-endpoint-ui-common": "link:x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common", + "@kbn/inference-langchain": "link:x-pack/platform/packages/shared/ai-infra/inference-langchain", "@kbn/inference-plugin": "link:x-pack/platform/plugins/shared/inference", "@kbn/inference_integration_flyout": "link:x-pack/platform/packages/private/ml/inference_integration_flyout", "@kbn/infra-forge": "link:x-pack/platform/packages/private/kbn-infra-forge", @@ -708,7 +709,6 @@ "@kbn/observability-onboarding-plugin": "link:x-pack/solutions/observability/plugins/observability_onboarding", "@kbn/observability-plugin": "link:x-pack/solutions/observability/plugins/observability", "@kbn/observability-shared-plugin": "link:x-pack/solutions/observability/plugins/observability_shared", - "@kbn/observability-synthetics-test-data": "link:x-pack/solutions/observability/packages/synthetics_test_data", "@kbn/observability-utils-browser": "link:x-pack/solutions/observability/packages/utils_browser", "@kbn/observability-utils-common": "link:x-pack/solutions/observability/packages/utils_common", "@kbn/observability-utils-server": "link:x-pack/solutions/observability/packages/utils_server", @@ -868,7 +868,6 @@ "@kbn/serverless-common-settings": "link:src/platform/packages/private/serverless/settings/common", "@kbn/serverless-observability": "link:x-pack/solutions/observability/plugins/serverless_observability", "@kbn/serverless-observability-settings": "link:src/platform/packages/shared/serverless/settings/observability_project", - "@kbn/serverless-project-switcher": "link:src/platform/packages/private/serverless/project_switcher", "@kbn/serverless-search": "link:x-pack/solutions/search/plugins/serverless_search", "@kbn/serverless-search-settings": "link:src/platform/packages/shared/serverless/settings/search_project", "@kbn/serverless-security-settings": "link:src/platform/packages/shared/serverless/settings/security_project", @@ -1184,7 +1183,7 @@ "minimatch": "^3.1.2", "moment": "^2.30.1", "moment-duration-format": "^2.3.2", - "moment-timezone": "^0.5.46", + "moment-timezone": "^0.5.47", "monaco-editor": "^0.44.0", "monaco-yaml": "^5.1.0", "murmurhash": "^2.0.1", @@ -1195,7 +1194,7 @@ "nodemailer": "^6.9.15", "normalize-path": "^3.0.0", "nunjucks": "^3.2.4", - "oas": "^25.2.1", + "oas": "^25.3.0", "object-hash": "^3.0.0", "object-path-immutable": "^3.1.1", "openai": "^4.72.0", @@ -1231,7 +1230,7 @@ "react-popper-tooltip": "^4.4.2", "react-recompose": "^0.33.0", "react-redux": "^7.2.8", - "react-reverse-portal": "^2.1.2", + "react-reverse-portal": "^2.2.0", "react-router": "^5.3.4", "react-router-config": "^5.1.1", "react-router-dom": "^5.3.4", @@ -1302,7 +1301,8 @@ "yaml": "^2.5.1", "yauzl": "^2.10.0", "yazl": "^2.5.1", - "zod": "^3.22.3" + "zod": "^3.22.3", + "zod-to-json-schema": "^3.23.0" }, "devDependencies": { "@apidevtools/swagger-parser": "^10.1.1", @@ -1468,6 +1468,7 @@ "@kbn/manifest": "link:packages/kbn-manifest", "@kbn/mock-idp-plugin": "link:packages/kbn-mock-idp-plugin", "@kbn/mock-idp-utils": "link:packages/kbn-mock-idp-utils", + "@kbn/observability-synthetics-test-data": "link:x-pack/solutions/observability/packages/synthetics_test_data", "@kbn/openapi-bundler": "link:packages/kbn-openapi-bundler", "@kbn/openapi-generator": "link:packages/kbn-openapi-generator", "@kbn/optimizer": "link:packages/kbn-optimizer", @@ -1758,7 +1759,7 @@ "fetch-mock": "^10.1.0", "file-loader": "^4.2.0", "find-cypress-specs": "^1.41.4", - "form-data": "^4.0.0", + "form-data": "^4.0.1", "geckodriver": "^5.0.0", "gulp-brotli": "^3.0.0", "gulp-postcss": "^9.0.1", @@ -1872,8 +1873,7 @@ "xml-crypto": "^6.0.0", "xmlbuilder": "15.1.1", "yargs": "^15.4.1", - "yarn-deduplicate": "^6.0.2", - "zod-to-json-schema": "^3.23.0" + "yarn-deduplicate": "^6.0.2" }, "packageManager": "yarn@1.22.21" } \ No newline at end of file diff --git a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts index e9e5add3db075..d7f3bb3ce3cfd 100644 --- a/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts +++ b/packages/kbn-apm-synthtrace/src/lib/apm/client/apm_synthtrace_kibana_client.ts @@ -53,6 +53,11 @@ export class ApmSynthtraceKibanaClient { ); } + // Add support for 7.x stack as latest version is available under different node + if (responseJson.response && responseJson.response.latestVersion) { + return responseJson.response.latestVersion as string; + } + return responseJson.item.latestVersion as string; }; diff --git a/packages/kbn-babel-preset/styled_components_files.js b/packages/kbn-babel-preset/styled_components_files.js index a51ab690e7674..bdec15873ab47 100644 --- a/packages/kbn-babel-preset/styled_components_files.js +++ b/packages/kbn-babel-preset/styled_components_files.js @@ -260,7 +260,6 @@ module.exports = { /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]common[\/\\]mock[\/\\]test_providers.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]eql_query_edit[\/\\]eql_overview_link.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]ml_job_link[\/\\]ml_job_link.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]ml_jobs_description[\/\\]ml_job_item.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]rule_actions_field[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]step_about_rule_details[\/\\]index.test.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]detection_engine[\/\\]rule_creation[\/\\]components[\/\\]step_content_wrapper[\/\\]index.tsx/, @@ -351,10 +350,8 @@ module.exports = { /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]entity_analytics[\/\\]components[\/\\]severity[\/\\]common[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]entity_analytics[\/\\]components[\/\\]severity[\/\\]severity_bar.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]entity_analytics[\/\\]components[\/\\]styled_basic_table.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]exceptions[\/\\]components[\/\\]exceptions_list_card[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]exceptions[\/\\]components[\/\\]exceptions_utility[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]exceptions[\/\\]components[\/\\]shared_list_utilty_bar[\/\\]index.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]exceptions[\/\\]pages[\/\\]shared_lists[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]explore[\/\\]components[\/\\]paginated_table[\/\\]index.test.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]explore[\/\\]components[\/\\]paginated_table[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]flyout[\/\\]document_details[\/\\]left[\/\\]components[\/\\]investigation_guide_view.tsx/, @@ -516,8 +513,6 @@ module.exports = { /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]timelines[\/\\]components[\/\\]timeline[\/\\]unified_components[\/\\]data_table[\/\\]custom_timeline_data_grid_body.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]timelines[\/\\]components[\/\\]timeline[\/\\]unified_components[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]security_solution[\/\\]public[\/\\]timelines[\/\\]components[\/\\]timeline[\/\\]unified_components[\/\\]styles.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]timelines[\/\\]public[\/\\]components[\/\\]hover_actions[\/\\]actions[\/\\]overflow.tsx/, - /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]timelines[\/\\]public[\/\\]components[\/\\]loading[\/\\]index.tsx/, /x-pack[\/\\]solutions[\/\\]security[\/\\]plugins[\/\\]timelines[\/\\]public[\/\\]mock[\/\\]test_providers.tsx/, /x-pack[\/\\]test[\/\\]plugin_functional[\/\\]plugins[\/\\]resolver_test[\/\\]public[\/\\]applications[\/\\]resolver_test[\/\\]index.tsx/, /src[\/\\]platform[\/\\]packages[\/\\]shared[\/\\]react[\/\\]kibana_context[\/\\]styled[\/\\]styled_provider.tsx/, diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index be5d77ad2e8c0..d8bebc391f0fb 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -2115,6 +2115,7 @@ } }, "ingest-outputs": { + "dynamic": false, "properties": { "allow_edit": { "enabled": false diff --git a/packages/kbn-eslint-config/.eslintrc.js b/packages/kbn-eslint-config/.eslintrc.js index f5e44037ea0a6..4e81270da9367 100644 --- a/packages/kbn-eslint-config/.eslintrc.js +++ b/packages/kbn-eslint-config/.eslintrc.js @@ -322,6 +322,7 @@ module.exports = { '@kbn/eslint/no_async_promise_body': 'error', '@kbn/eslint/no_async_foreach': 'error', '@kbn/eslint/no_deprecated_authz_config': 'error', + '@kbn/eslint/require_kibana_feature_privileges_naming': 'warn', '@kbn/eslint/no_trailing_import_slash': 'error', '@kbn/eslint/no_constructor_args_in_property_initializers': 'error', '@kbn/eslint/no_this_in_property_initializers': 'error', diff --git a/packages/kbn-eslint-plugin-eslint/index.js b/packages/kbn-eslint-plugin-eslint/index.js index 8c52f916ec206..2eeb718ad1602 100644 --- a/packages/kbn-eslint-plugin-eslint/index.js +++ b/packages/kbn-eslint-plugin-eslint/index.js @@ -21,5 +21,6 @@ module.exports = { no_unsafe_console: require('./rules/no_unsafe_console'), no_unsafe_hash: require('./rules/no_unsafe_hash'), no_deprecated_authz_config: require('./rules/no_deprecated_authz_config'), + require_kibana_feature_privileges_naming: require('./rules/require_kibana_feature_privileges_naming'), }, }; diff --git a/packages/kbn-eslint-plugin-eslint/rules/require_kibana_feature_privileges_naming.js b/packages/kbn-eslint-plugin-eslint/rules/require_kibana_feature_privileges_naming.js new file mode 100644 index 0000000000000..9513b2b3f280f --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/require_kibana_feature_privileges_naming.js @@ -0,0 +1,223 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +const ts = require('typescript'); +const path = require('path'); + +function getImportedVariableValue(context, name, propertyName) { + const parent = context + .getAncestors() + .find((ancestor) => ['BlockStatement', 'Program'].includes(ancestor.type)); + + if (!parent) return; + + const importDeclaration = parent.body.find( + (statement) => + statement.type === 'ImportDeclaration' && + statement.specifiers.some((specifier) => specifier.local.name === name) + ); + + if (!importDeclaration) return; + + const absoluteImportPath = require.resolve(importDeclaration.source.value, { + paths: [path.dirname(context.getFilename())], + }); + + const program = ts.createProgram([absoluteImportPath], {}); + const sourceFile = program.getSourceFile(absoluteImportPath); + + if (!sourceFile) return null; + + const checker = program.getTypeChecker(); + const symbols = checker.getExportsOfModule(sourceFile.symbol); + const symbol = symbols.find((s) => s.name === name); + + if (!symbol) return null; + + if (propertyName) { + const currentSymbol = checker.getTypeOfSymbolAtLocation(symbol, sourceFile); + const property = currentSymbol.getProperty(propertyName); + + if (ts.isStringLiteral(property.valueDeclaration.initializer)) { + return property.valueDeclaration.initializer.text; + } + + return null; + } + + const initializer = symbol?.valueDeclaration?.initializer; + + if (ts.isStringLiteral(initializer)) { + return initializer.text; + } + + return null; +} + +function validatePrivilegesNode(context, privilegesNode, scopedVariables) { + ['all', 'read'].forEach((privilegeType) => { + const privilege = privilegesNode.value.properties.find( + (prop) => + prop.key && prop.key.name === privilegeType && prop.value.type === 'ObjectExpression' + ); + + if (!privilege) return; + + const apiProperty = privilege.value.properties.find( + (prop) => prop.key && prop.key.name === 'api' && prop.value.type === 'ArrayExpression' + ); + + if (!apiProperty) return; + + apiProperty.value.elements.forEach((element) => { + let valueToCheck = null; + + if (element.type === 'Literal' && typeof element.value === 'string') { + valueToCheck = element.value; + } else if (element.type === 'Identifier') { + valueToCheck = scopedVariables.has(element.name) + ? scopedVariables.get(element.name) + : getImportedVariableValue(context, element.name); + } else if (element.type === 'MemberExpression') { + valueToCheck = getImportedVariableValue( + context, + element.object.name, + element.property.name + ); + } + + if (valueToCheck) { + const isValid = /^(manage|create|update|delete|read)/.test(valueToCheck); + const usesValidSeparator = /^[a-z0-9_]+$/.test(valueToCheck); + let method = 'manage'; + + if (valueToCheck.includes('read')) { + method = 'read'; + } + + if (valueToCheck.includes('create') || valueToCheck.includes('copy')) { + method = 'create'; + } + + if (valueToCheck.includes('delete')) { + method = 'delete'; + } + + if (valueToCheck.includes('update')) { + method = 'update'; + } + + if (!isValid) { + return context.report({ + node: element, + message: `API privilege '${valueToCheck}' should start with [manage|create|update|delete|read] or use ApiPrivileges.${method} instead`, + }); + } + + if (!usesValidSeparator) { + return context.report({ + node: element, + message: `API privilege '${valueToCheck}' should use '_' as a separator`, + }); + } + } + }); + }); +} + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Ensure API privileges in registerKibanaFeature call follow naming conventions', + category: 'Best Practices', + recommended: true, + }, + schema: [], + }, + + create(context) { + return { + CallExpression(node) { + const isRegisterKibanaFeatureCall = + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'registerKibanaFeature' && + ((node.callee.object.type === 'MemberExpression' && + node.callee.object.property.name === 'features') || + node.callee.object.name === 'features'); + + if (!isRegisterKibanaFeatureCall) return; + + const scopedVariables = new Map(); + + const sourceCode = context.getSourceCode(); + + const parent = sourceCode + .getAncestors(node) + .find((ancestor) => ['BlockStatement', 'Program'].includes(ancestor.type)); + + if (parent) { + parent.body.forEach((statement) => { + if (statement.type === 'VariableDeclaration') { + statement.declarations.forEach((declaration) => { + if ( + declaration.id.type === 'Identifier' && + declaration.init && + declaration.init.type === 'Literal' && + typeof declaration.init.value === 'string' + ) { + scopedVariables.set(declaration.id.name, declaration.init.value); + } + }); + } + }); + } + + const [feature] = node.arguments; + if (feature?.type === 'ObjectExpression') { + const privilegesProperty = feature.properties.find( + (prop) => + prop.key && prop.key.name === 'privileges' && prop.value.type === 'ObjectExpression' + ); + + if (!privilegesProperty) return; + + return validatePrivilegesNode(context, privilegesProperty, scopedVariables); + } + }, + ExportNamedDeclaration(node) { + if ( + node.declaration?.type !== 'VariableDeclaration' || + !node.declaration.declarations?.length + ) { + return; + } + + node.declaration.declarations.forEach((declaration) => { + if (declaration.init && declaration.init.type === 'ObjectExpression') { + if ( + !['id', 'name', 'privileges', 'scope', 'category'].every((key) => + declaration.init.properties.find((prop) => prop.key?.name === key) + ) + ) { + return; + } + + const privilegesProperty = declaration.init.properties.find( + (prop) => + prop.key && prop.key.name === 'privileges' && prop.value.type === 'ObjectExpression' + ); + + validatePrivilegesNode(context, privilegesProperty, new Map()); + } + }); + }, + }; + }, +}; diff --git a/packages/kbn-eslint-plugin-eslint/rules/require_kibana_feature_privileges_naming.test.js b/packages/kbn-eslint-plugin-eslint/rules/require_kibana_feature_privileges_naming.test.js new file mode 100644 index 0000000000000..00bd71c85f059 --- /dev/null +++ b/packages/kbn-eslint-plugin-eslint/rules/require_kibana_feature_privileges_naming.test.js @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ +const { RuleTester } = require('eslint'); +const rule = require('./require_kibana_feature_privileges_naming'); + +const ruleTester = new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 2018, + ecmaFeatures: { + jsx: true, + }, + }, +}); + +ruleTester.run('@kbn/require_kibana_feature_privileges_naming', rule, { + valid: [ + { + code: ` + const privilege = "manage_users"; + plugins.features.registerKibanaFeature({ + privileges: { + all: { + api: [privilege, "create_logs", "read_logs"], + }, + }, + }); + `, + }, + { + code: ` + plugins.features.registerKibanaFeature({ + privileges: { + all: { + api: ["manage_logs", "create_entries"], + }, + }, + }); + `, + }, + { + code: ` + features.registerKibanaFeature({ + privileges: { + all: { + api: ["read_entries", "update_entries"], + }, + }, + }); + `, + }, + { + code: ` + const validPrivilege = "delete_users"; + const anotherValidPrivilege = "manage_permissions"; + plugins.features.registerKibanaFeature({ + privileges: { + all: { + api: [validPrivilege, anotherValidPrivilege], + }, + }, + }); + `, + }, + ], + invalid: [ + { + code: ` + plugins.features.registerKibanaFeature({ + privileges: { + all: { + api: ["incorrect_value", "manage_logs"], + }, + }, + }); + `, + errors: [ + { + message: `API privilege 'incorrect_value' should start with [manage|create|update|delete|read] or use ApiPrivileges.manage instead`, + }, + ], + }, + { + code: ` + features.registerKibanaFeature({ + privileges: { + all: { + api: ["entry_read", "create_logs"], + }, + }, + }); + `, + errors: [ + { + message: `API privilege 'entry_read' should start with [manage|create|update|delete|read] or use ApiPrivileges.read instead`, + }, + ], + }, + { + code: ` + features.registerKibanaFeature({ + privileges: { + all: { + api: ["read_entry-log", "create_logs"], + }, + }, + }); + `, + errors: [ + { + message: `API privilege 'read_entry-log' should use '_' as a separator`, + }, + ], + }, + { + code: ` + const privilege = 'users-manage'; + plugins.features.registerKibanaFeature({ + privileges: { + all: { + api: [privilege, "create_logs", "read_logs"], + }, + }, + }); + `, + errors: [ + { + message: `API privilege 'users-manage' should start with [manage|create|update|delete|read] or use ApiPrivileges.manage instead`, + }, + ], + }, + ], +}); diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 0fe04b0c1dcca..156b44e2b4526 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -96,7 +96,7 @@ pageLoadAssetSize: lens: 57135 licenseManagement: 41817 licensing: 29004 - links: 44490 + links: 8000 lists: 22900 logsDataAccess: 16759 logsExplorer: 60000 @@ -156,7 +156,7 @@ pageLoadAssetSize: serverlessSearch: 72995 sessionView: 77750 share: 88160 - slo: 37039 + slo: 45000 snapshotRestore: 79032 spaces: 57868 stackAlerts: 58316 diff --git a/renovate.json b/renovate.json index 0bfaa0a198fb4..b15cf62d6a4f8 100644 --- a/renovate.json +++ b/renovate.json @@ -2837,7 +2837,6 @@ "cypress" ], "reviewers": [ - "team:apm", "team:security-solution" ], "matchBaseBranches": [ diff --git a/src/cli/serve/compile_config_stack.js b/src/cli/serve/compile_config_stack.js index 085a21ce954cd..14e88630c71b7 100644 --- a/src/cli/serve/compile_config_stack.js +++ b/src/cli/serve/compile_config_stack.js @@ -9,7 +9,7 @@ import _ from 'lodash'; -import { readFileSync, writeFileSync, statSync, existsSync } from 'fs'; +import { statSync } from 'fs'; import { resolve } from 'path'; import { getConfigPath, getConfigDirectory } from '@kbn/utils'; import { getConfigFromFiles } from '@kbn/config'; @@ -37,11 +37,6 @@ export function compileConfigStack({ configOverrides, devConfig, dev, serverless configs.push(resolveConfig('kibana.dev.yml')); } - if (dev && serverless) { - writeProjectSwitcherConfig('serverless.recent.dev.yml', serverless); - configs.push(resolveConfig('serverless.recent.dev.yml')); - } - // Filter out all config paths that didn't exist configs = configs.filter(isNotNull); @@ -82,27 +77,6 @@ function resolveConfig(fileName) { } } -/** - * @param {string} fileName - * @param {object} opts - */ -function writeProjectSwitcherConfig(fileName, serverlessOption) { - const path = resolve(getConfigDirectory(), fileName); - const configAlreadyExists = existsSync(path); - - const preserveExistingConfig = serverlessOption === true; - const serverlessMode = validateServerlessMode(serverlessOption) || 'es'; - - if (configAlreadyExists && preserveExistingConfig) { - return; - } else { - const content = `xpack.serverless.plugin.developer.projectSwitcher.enabled: true\nserverless: ${serverlessMode}\n`; - if (!configAlreadyExists || readFileSync(path).toString() !== content) { - writeFileSync(path, content); - } - } -} - /** * @param {string} filePath Path to the config file * @returns {boolean} Whether the file exists @@ -143,7 +117,6 @@ function validateServerlessMode(serverlessMode) { } if (serverlessMode === true) { - // Defaulting to read the project-switcher's settings in `serverless.recent.dev.yml` return null; } diff --git a/src/cli/serve/compile_config_stack.test.js b/src/cli/serve/compile_config_stack.test.js index 99f38b12a51a3..0ce307b9ae30a 100644 --- a/src/cli/serve/compile_config_stack.test.js +++ b/src/cli/serve/compile_config_stack.test.js @@ -15,7 +15,7 @@ jest.mock('@kbn/repo-info', () => ({ })); jest.mock('@kbn/config'); -import { statSync, existsSync, writeFileSync } from 'fs'; +import { statSync } from 'fs'; import { getConfigFromFiles } from '@kbn/config'; import { compileConfigStack } from './compile_config_stack'; @@ -69,20 +69,12 @@ describe('compileConfigStack', () => { 'serverless.security.yml', 'kibana.yml', 'kibana.dev.yml', - 'serverless.recent.dev.yml', 'serverless.dev.yml', 'serverless.security.dev.yml', ]); }); it('defaults to "es" if --serverless and --dev are there', async () => { - existsSync.mockImplementationOnce((filename) => { - if (Path.basename(filename) === 'serverless.recent.dev.yml') { - return false; - } else { - return true; - } - }); getConfigFromFiles.mockImplementationOnce(() => { return { serverless: 'es', @@ -94,55 +86,15 @@ describe('compileConfigStack', () => { serverless: true, }).map(toFileNames); - expect(existsSync).toHaveBeenCalledWith( - '/some/imaginary/path/config/serverless.recent.dev.yml' - ); - expect(writeFileSync).toHaveBeenCalledWith( - '/some/imaginary/path/config/serverless.recent.dev.yml', - expect.stringContaining('serverless: es') - ); expect(configList).toEqual([ 'serverless.yml', 'serverless.es.yml', 'kibana.yml', 'kibana.dev.yml', - 'serverless.recent.dev.yml', 'serverless.dev.yml', 'serverless.es.dev.yml', ]); }); - - it('respects persisted project-switcher decision when --serverless && --dev true', async () => { - existsSync.mockImplementationOnce((filename) => { - if (Path.basename(filename) === 'serverless.recent.dev.yml') { - return true; - } - }); - getConfigFromFiles.mockImplementationOnce(() => { - return { - serverless: 'oblt', - }; - }); - - const configList = compileConfigStack({ - dev: true, - serverless: true, - }).map(toFileNames); - - expect(existsSync).toHaveBeenCalledWith( - '/some/imaginary/path/config/serverless.recent.dev.yml' - ); - expect(writeFileSync).not.toHaveBeenCalled(); - expect(configList).toEqual([ - 'serverless.yml', - 'serverless.oblt.yml', - 'kibana.yml', - 'kibana.dev.yml', - 'serverless.recent.dev.yml', - 'serverless.dev.yml', - 'serverless.oblt.dev.yml', - ]); - }); }); function toFileNames(path) { diff --git a/src/cli/serve/integration_tests/serverless_config_flag.test.ts b/src/cli/serve/integration_tests/serverless_config_flag.test.ts index 9ec6f441c6c9b..faaeb293489c6 100644 --- a/src/cli/serve/integration_tests/serverless_config_flag.test.ts +++ b/src/cli/serve/integration_tests/serverless_config_flag.test.ts @@ -9,12 +9,9 @@ import { spawn, spawnSync, ChildProcessWithoutNullStreams } from 'child_process'; import type { Readable } from 'stream'; -import { readFileSync } from 'fs'; -import { resolve } from 'path'; import { filter, firstValueFrom, from, concatMap } from 'rxjs'; import { REPO_ROOT } from '@kbn/repo-info'; -import { getConfigDirectory } from '@kbn/utils'; // Failing: See https://github.com/elastic/kibana/issues/163257 // Failing: See https://github.com/elastic/kibana/issues/163258 @@ -63,24 +60,6 @@ describe.skip('cli serverless project type', () => { 20 * 1000 ); - it.each(['es', 'oblt', 'security'])( - 'writes the serverless project type %s in config/serverless.recent.dev.yml', - async (mode) => { - // Making sure `--serverless` translates into the `serverless` config entry, and validates against the accepted values - child = spawn(process.execPath, ['scripts/kibana', '--dev', `--serverless=${mode}`], { - cwd: REPO_ROOT, - }); - - // Wait until Kibana starts bootstrapping (at that point the file should be present) - const found = await waitForMessage(child.stdout, 'Kibana process configured with roles'); - expect(found).not.toContain('FATAL'); - - expect( - readFileSync(resolve(getConfigDirectory(), 'serverless.recent.dev.yml'), 'utf-8') - ).toContain(`serverless: ${mode}\n`); - } - ); - it.each(['es', 'oblt', 'security'])( 'Kibana does not crash when running project type %s', async (mode) => { diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index c202529d692c2..d519769b09038 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -126,7 +126,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", "ingest-agent-policies": "57ebfb047cf0b81c6fa0ceed8586fa7199c7c5e2", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", - "ingest-outputs": "55988d5f778bbe0e76caa7e6468707a0a056bdd8", + "ingest-outputs": "6743521f501bd77b1523dbb1df48d7c47fdad529", "ingest-package-policies": "870f8c21fe3602f31075430a1fdfb052c62d4a14", "ingest_manager_settings": "111a616eb72627c002029c19feb9e6c439a10505", "inventory-view": "fd2b7fe713956f261018dded00d8f8c986417763", diff --git a/src/platform/packages/private/kbn-esql-editor/src/editor_footer/errors_warnings_popover.tsx b/src/platform/packages/private/kbn-esql-editor/src/editor_footer/errors_warnings_popover.tsx index b531b79ed909c..5d0cdba8d01c5 100644 --- a/src/platform/packages/private/kbn-esql-editor/src/editor_footer/errors_warnings_popover.tsx +++ b/src/platform/packages/private/kbn-esql-editor/src/editor_footer/errors_warnings_popover.tsx @@ -87,7 +87,12 @@ function ErrorsWarningsContent({ - + {item.message} diff --git a/src/platform/packages/private/kbn-grid-layout/grid/grid_height_smoother.tsx b/src/platform/packages/private/kbn-grid-layout/grid/grid_height_smoother.tsx index 213b1dc80a63d..792d8bb1aabe7 100644 --- a/src/platform/packages/private/kbn-grid-layout/grid/grid_height_smoother.tsx +++ b/src/platform/packages/private/kbn-grid-layout/grid/grid_height_smoother.tsx @@ -33,14 +33,11 @@ export const GridHeightSmoother = ({ if (!interactionEvent) { smoothHeightRef.current.style.minHeight = `${dimensions.height}px`; - smoothHeightRef.current.style.userSelect = 'auto'; return; } - smoothHeightRef.current.style.minHeight = `${ smoothHeightRef.current.getBoundingClientRect().height }px`; - smoothHeightRef.current.style.userSelect = 'none'; }); return () => { diff --git a/src/platform/packages/private/kbn-grid-layout/grid/grid_layout.tsx b/src/platform/packages/private/kbn-grid-layout/grid/grid_layout.tsx index 1d285046f89f4..f42d61321ad59 100644 --- a/src/platform/packages/private/kbn-grid-layout/grid/grid_layout.tsx +++ b/src/platform/packages/private/kbn-grid-layout/grid/grid_layout.tsx @@ -161,6 +161,12 @@ export const GridLayout = ({ css={css` padding: calc(var(--kbnGridGutterSize) * 1px); + // disable pointer events and user select on drag + resize + &:has(.kbnGridPanel--active) { + user-select: none; + pointer-events: none; + } + &:has(.kbnGridPanel--expanded) { ${expandedPanelStyles} } diff --git a/src/platform/packages/private/kbn-language-documentation/src/sections/generated/scalar_functions.tsx b/src/platform/packages/private/kbn-language-documentation/src/sections/generated/scalar_functions.tsx index c372b496a7585..dc4fbad1a9ece 100644 --- a/src/platform/packages/private/kbn-language-documentation/src/sections/generated/scalar_functions.tsx +++ b/src/platform/packages/private/kbn-language-documentation/src/sections/generated/scalar_functions.tsx @@ -1384,6 +1384,9 @@ export const functions = { Match can be used on fields from the text family like text and semantic_text, as well as other field types like keyword, boolean, dates, and numeric types. + Match can use function named parameters to specify additional options for the match query. + All match query parameters are supported. + For a simplified syntax, you can use the match operator \`:\` operator instead of \`MATCH\`. \`MATCH\` returns true if the provided query matches the row. diff --git a/src/platform/packages/private/serverless/project_switcher/README.mdx b/src/platform/packages/private/serverless/project_switcher/README.mdx deleted file mode 100644 index 240988346458c..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/README.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: serverless/components/ProjectSwitcher -slug: /serverless/components/project-switcher -title: Project Switcher -description: A popup which allows a developer to switch between project types on their dev server. -tags: ['serverless', 'component'] -date: 2023-04-23 ---- - -When working on Serverless instances of Kibana, developers likely want to switch between different project types to test changes. This Project Switcher is intended to be placed into the header bar by the Serverless plugin when the server is in development mode to allow "quick switching" between configurations. - -The connected component uses `http` to post a selection to a given API endpoint, intended to alter the YML configuration and trigger Watcher to restart the server. To that end, it will post its message to a given API endpoint and replace the content of `document.body`. The remainder of the process is left to the Serverless plugin. diff --git a/src/platform/packages/private/serverless/project_switcher/kibana.jsonc b/src/platform/packages/private/serverless/project_switcher/kibana.jsonc deleted file mode 100644 index a0722bf662a4e..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/kibana.jsonc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "shared-common", - "id": "@kbn/serverless-project-switcher", - "owner": [ - "@elastic/appex-sharedux" - ], - "group": "platform", - "visibility": "private" -} \ No newline at end of file diff --git a/src/platform/packages/private/serverless/project_switcher/mocks/jest.mock.ts b/src/platform/packages/private/serverless/project_switcher/mocks/jest.mock.ts deleted file mode 100644 index acfacd676e639..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/mocks/jest.mock.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { Services, KibanaDependencies } from '../src/types'; - -export const getProjectSwitcherServicesMock: () => jest.Mocked = () => ({ - setProjectType: jest.fn(), -}); - -export const getProjectSwitcherKibanaDependenciesMock: () => jest.Mocked = - () => ({ - coreStart: { - http: { - post: jest.fn(() => Promise.resolve({ data: {} })), - }, - }, - projectChangeAPIUrl: 'serverless/change_project', - }); diff --git a/src/platform/packages/private/serverless/project_switcher/mocks/storybook.mock.ts b/src/platform/packages/private/serverless/project_switcher/mocks/storybook.mock.ts deleted file mode 100644 index 903897b6e9ef9..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/mocks/storybook.mock.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { action } from '@storybook/addon-actions'; -import { AbstractStorybookMock } from '@kbn/shared-ux-storybook-mock'; - -import type { ProjectSwitcherProps, Services } from '../src/types'; - -type PropArguments = Pick; - -/** - * Storybook parameters provided from the controls addon. - */ -export type ProjectSwitcherStorybookParams = Record; - -/** - * Storybook mocks for the `NoDataCard` component. - */ -export class ProjectSwitcherStorybookMock extends AbstractStorybookMock< - ProjectSwitcherProps, - Services, - PropArguments, - {} -> { - propArguments = { - currentProjectType: { - control: { type: 'radio' }, - options: ['observability', 'security', 'search'], - defaultValue: 'observability', - }, - }; - serviceArguments = {}; - dependencies = []; - - getProps(params?: ProjectSwitcherStorybookParams): ProjectSwitcherProps { - return { - currentProjectType: this.getArgumentValue('currentProjectType', params), - }; - } - - getServices(_params: ProjectSwitcherStorybookParams): Services { - return { - setProjectType: action('setProjectType'), - }; - } -} diff --git a/src/platform/packages/private/serverless/project_switcher/package.json b/src/platform/packages/private/serverless/project_switcher/package.json deleted file mode 100644 index 63edd8eb28746..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@kbn/serverless-project-switcher", - "private": true, - "version": "1.0.0", - "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "sideEffects": false -} \ No newline at end of file diff --git a/src/platform/packages/private/serverless/project_switcher/src/constants.ts b/src/platform/packages/private/serverless/project_switcher/src/constants.ts deleted file mode 100644 index a31b3e6909197..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/constants.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import type { IconType } from '@elastic/eui'; -import type { ProjectType } from '@kbn/serverless-types'; - -export const icons: Record = { - observability: 'logoObservability', - security: 'logoSecurity', - search: 'logoEnterpriseSearch', -} as const; - -export const labels: Record = { - observability: 'Observability', - security: 'Security', - search: 'Enterprise Search', -} as const; - -export const projectTypes: ProjectType[] = ['security', 'observability', 'search']; diff --git a/src/platform/packages/private/serverless/project_switcher/src/header_button.tsx b/src/platform/packages/private/serverless/project_switcher/src/header_button.tsx deleted file mode 100644 index 3f3cb66635392..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/header_button.tsx +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { MouseEventHandler } from 'react'; -import { EuiHeaderSectionItemButton, EuiIcon } from '@elastic/eui'; - -import { ProjectType } from '@kbn/serverless-types'; - -import { icons } from './constants'; - -export const TEST_ID = 'projectSwitcherButton'; - -export interface Props { - onClick: MouseEventHandler; - currentProjectType: ProjectType; -} - -export const HeaderButton = ({ onClick, currentProjectType }: Props) => ( - - - -); diff --git a/src/platform/packages/private/serverless/project_switcher/src/item.tsx b/src/platform/packages/private/serverless/project_switcher/src/item.tsx deleted file mode 100644 index e83089bee3cd4..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/item.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { EuiIcon, EuiKeyPadMenuItem, type EuiIconProps } from '@elastic/eui'; -import { ProjectType } from '@kbn/serverless-types'; - -import { labels, icons } from './constants'; - -type OnChangeType = (id: string, value?: any) => void; - -interface ItemProps extends Pick { - type: ProjectType; - onChange: (type: ProjectType) => void; - isSelected: boolean; -} - -export const SwitcherItem = ({ type: id, onChange, isSelected }: ItemProps) => ( - - - -); diff --git a/src/platform/packages/private/serverless/project_switcher/src/logo.tsx b/src/platform/packages/private/serverless/project_switcher/src/logo.tsx deleted file mode 100644 index 815c7fe784694..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/logo.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { EuiIcon } from '@elastic/eui'; -import type { ProjectType } from '@kbn/serverless-types'; - -export interface Props { - project: ProjectType; -} - -export const Logo = ({ project }: Props) => { - let type = 'logoElastic'; - switch (project) { - case 'search': - type = 'logoElasticsearch'; - break; - case 'security': - type = 'logoSecurity'; - break; - case 'observability': - type = 'logoObservability'; - break; - } - - return ; -}; diff --git a/src/platform/packages/private/serverless/project_switcher/src/services.tsx b/src/platform/packages/private/serverless/project_switcher/src/services.tsx deleted file mode 100644 index b78b9b16207e9..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/services.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { FC, PropsWithChildren, useContext } from 'react'; -import ReactDOM from 'react-dom'; -import { Loader } from './loader'; - -import type { Services, KibanaDependencies } from './types'; - -const Context = React.createContext(null); - -/** - * A Context Provider that provides services to the component and its dependencies. - */ -export const ProjectSwitcherProvider: FC> = ({ - children, - ...services -}) => { - return {children}; -}; - -/** - * Kibana-specific Provider that maps dependencies to services. - */ -export const ProjectSwitcherKibanaProvider: FC> = ({ - children, - coreStart, - projectChangeAPIUrl, -}) => { - const value: Services = { - setProjectType: (projectType) => { - coreStart.http - .post(projectChangeAPIUrl, { body: JSON.stringify({ id: projectType }) }) - .then(() => { - ReactDOM.render(, document.body); - - // Give the watcher a couple of seconds to see the file change. - setTimeout(() => { - window.location.href = '/'; - }, 2000); - }); - }, - }; - - return {children}; -}; - -/** - * React hook for accessing pre-wired services. - */ -export function useServices() { - const context = useContext(Context); - - if (!context) { - throw new Error( - 'ProjectSwitcher Context is missing. Ensure your component or React root is wrapped with ProjectSwitcherContext.' - ); - } - - return context; -} diff --git a/src/platform/packages/private/serverless/project_switcher/src/switcher.component.tsx b/src/platform/packages/private/serverless/project_switcher/src/switcher.component.tsx deleted file mode 100644 index d11314d551a61..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/switcher.component.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React, { useState } from 'react'; -import { css } from '@emotion/react'; -import { EuiPopover, useGeneratedHtmlId, EuiPopoverTitle, EuiKeyPadMenu } from '@elastic/eui'; - -import { ProjectType } from '@kbn/serverless-types'; - -import { SwitcherItem } from './item'; -import type { ProjectSwitcherComponentProps } from './types'; -import { HeaderButton } from './header_button'; -import { projectTypes } from './constants'; - -export { TEST_ID as TEST_ID_BUTTON } from './header_button'; -export const TEST_ID_ITEM_GROUP = 'projectSwitcherItemGroup'; - -const switcherCSS = css` - min-width: 240px; -`; - -export const ProjectSwitcher = ({ - currentProjectType, - onProjectChange, -}: ProjectSwitcherComponentProps) => { - const [isOpen, setIsOpen] = useState(false); - const id = useGeneratedHtmlId({ - prefix: 'switcherPopover', - }); - - const closePopover = () => { - setIsOpen(false); - }; - - const onButtonClick = () => { - setIsOpen(!isOpen); - }; - - const onChange = (projectType: ProjectType) => { - closePopover(); - onProjectChange(projectType); - return false; - }; - - const items = projectTypes.map((type) => ( - - )); - - const button = ; - - return ( - - Switch Project Type - - {items} - - - ); -}; diff --git a/src/platform/packages/private/serverless/project_switcher/src/switcher.stories.tsx b/src/platform/packages/private/serverless/project_switcher/src/switcher.stories.tsx deleted file mode 100644 index 36e782f9fc01b..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/switcher.stories.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; - -import { - ProjectSwitcherStorybookMock, - type ProjectSwitcherStorybookParams, -} from '../mocks/storybook.mock'; - -import { ProjectSwitcher as Component } from './switcher'; -import { ProjectSwitcherProvider as Provider } from './services'; - -import mdx from '../README.mdx'; - -export default { - title: 'Developer/Project Switcher', - description: '', - parameters: { - docs: { - page: mdx, - }, - }, -}; - -const mock = new ProjectSwitcherStorybookMock(); -const argTypes = mock.getArgumentTypes(); - -export const ProjectSwitcher = (params: ProjectSwitcherStorybookParams) => { - return ( - - - - ); -}; - -ProjectSwitcher.argTypes = argTypes; diff --git a/src/platform/packages/private/serverless/project_switcher/src/switcher.test.tsx b/src/platform/packages/private/serverless/project_switcher/src/switcher.test.tsx deleted file mode 100644 index c227e0c2e7c9d..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/switcher.test.tsx +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { render, RenderResult, screen, within, cleanup } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { ProjectType } from '@kbn/serverless-types'; - -import { ProjectSwitcherKibanaProvider, ProjectSwitcherProvider } from './services'; -import { - getProjectSwitcherKibanaDependenciesMock, - getProjectSwitcherServicesMock, -} from '../mocks/jest.mock'; -import { ProjectSwitcher } from './switcher'; -import { - ProjectSwitcher as ProjectSwitcherComponent, - TEST_ID_BUTTON, - TEST_ID_ITEM_GROUP, -} from './switcher.component'; -import { KibanaDependencies, Services } from './types'; - -const renderKibanaProjectSwitcher = ( - currentProjectType: ProjectType = 'observability' -): [RenderResult, jest.Mocked] => { - const mock = getProjectSwitcherKibanaDependenciesMock(); - return [ - render( - - - - ), - mock, - ]; -}; - -const renderProjectSwitcher = ( - currentProjectType: ProjectType = 'observability' -): [RenderResult, jest.Mocked] => { - const mock = getProjectSwitcherServicesMock(); - return [ - render( - - - - ), - mock, - ]; -}; - -describe('ProjectSwitcher', () => { - describe('Component', () => { - test('is rendered', () => { - expect(() => - render( - - ) - ).not.toThrowError(); - }); - }); - - describe('Connected Component', () => { - beforeEach(() => { - jest.spyOn(console, 'error').mockImplementation(() => {}); - }); - - test("doesn't render if the Provider is missing", () => { - expect(() => render()).toThrowError(); - }); - - describe('with Services', () => { - test('is rendered', () => { - renderProjectSwitcher(); - const button = screen.queryByTestId(TEST_ID_BUTTON); - expect(button).not.toBeNull(); - }); - - test('opens', async () => { - renderProjectSwitcher(); - - let group = screen.queryByTestId(TEST_ID_ITEM_GROUP); - expect(group).toBeNull(); - - const button = screen.getByTestId(TEST_ID_BUTTON); - await userEvent.click(button); - - group = screen.queryByTestId(TEST_ID_ITEM_GROUP); - expect(group).not.toBeNull(); - }); - - test('calls setProjectType when clicked', async () => { - const [_, mock] = renderProjectSwitcher(); - - const button = screen.getByTestId(TEST_ID_BUTTON); - await userEvent.click(button); - - const group = screen.getByTestId(TEST_ID_ITEM_GROUP); - const project = await within(group).findByLabelText('Security'); - await userEvent.click(project); - - expect(mock.setProjectType).toHaveBeenCalled(); - }); - }); - }); - - describe('with Kibana Dependencies', () => { - beforeEach(() => { - cleanup(); - }); - - test('is rendered', () => { - renderKibanaProjectSwitcher(); - const button = screen.queryByTestId(TEST_ID_BUTTON); - expect(button).not.toBeNull(); - }); - - test('opens', async () => { - renderKibanaProjectSwitcher(); - - let group = screen.queryByTestId(TEST_ID_ITEM_GROUP); - expect(group).toBeNull(); - - const button = screen.getByTestId(TEST_ID_BUTTON); - await userEvent.click(button); - - group = screen.queryByTestId(TEST_ID_ITEM_GROUP); - expect(group).not.toBeNull(); - }); - - test('posts message to change project', async () => { - const [_, mock] = renderKibanaProjectSwitcher(); - - const button = screen.getByTestId(TEST_ID_BUTTON); - await userEvent.click(button); - - const group = screen.getByTestId(TEST_ID_ITEM_GROUP); - const project = await within(group).findByLabelText('Security'); - await userEvent.click(project); - - expect(mock.coreStart.http.post).toHaveBeenCalled(); - }); - }); -}); diff --git a/src/platform/packages/private/serverless/project_switcher/src/switcher.tsx b/src/platform/packages/private/serverless/project_switcher/src/switcher.tsx deleted file mode 100644 index 1ca458d5ec398..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/switcher.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import React from 'react'; -import { ProjectType } from '@kbn/serverless-types'; -import { ProjectSwitcher as Component } from './switcher.component'; - -import { useServices } from './services'; -import type { ProjectSwitcherProps } from './types'; - -export const ProjectSwitcher = (props: ProjectSwitcherProps) => { - const { setProjectType } = useServices(); - const onProjectChange = (projectType: ProjectType) => setProjectType(projectType); - - return ; -}; diff --git a/src/platform/packages/private/serverless/project_switcher/src/types.ts b/src/platform/packages/private/serverless/project_switcher/src/types.ts deleted file mode 100644 index 9e598d3dda19f..0000000000000 --- a/src/platform/packages/private/serverless/project_switcher/src/types.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { ProjectType } from '@kbn/serverless-types'; - -/** - * A list of services that are consumed by this component. - */ -export interface Services { - setProjectType: (projectType: ProjectType) => void; -} - -/** - * An interface containing a collection of Kibana plugins and services required to - * render this component. - */ -export interface KibanaDependencies { - coreStart: { - http: { - post: (path: string, options: { body: string }) => Promise; - }; - }; - projectChangeAPIUrl: string; -} - -/** - * Props for the `ProjectSwitcher` pure component. - */ -export interface ProjectSwitcherComponentProps { - onProjectChange: (projectType: ProjectType) => void; - currentProjectType: ProjectType; -} - -export type ProjectSwitcherProps = Pick; diff --git a/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts b/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts index ac314eee36f5b..19baacfc3fcf4 100644 --- a/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts +++ b/src/platform/packages/shared/chart-expressions-common/color_categories.test.ts @@ -7,49 +7,38 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { DatatableRow } from '@kbn/expressions-plugin/common'; +import { DatatableColumn, DatatableRow } from '@kbn/expressions-plugin/common'; import { getColorCategories } from './color_categories'; -const extensions = ['gz', 'css', '', 'rpm', 'deb', 'zip', null]; -const getExtension = (i: number) => extensions[i % extensions.length]; +const getNextExtension = (() => { + let i = 0; + const extensions = ['gz', 'css', '', 'rpm', 'deb', 'zip', null]; + return () => extensions[i++ % extensions.length]; +})(); -const basicDatatableRows: DatatableRow[] = Array.from({ length: 30 }).map((_, i) => ({ - count: i, - extension: getExtension(i), -})); - -const isTransposedDatatableRows: DatatableRow[] = Array.from({ length: 30 }).map((_, i) => ({ - count: i, - ['safari---extension']: getExtension(i), - ['chrome---extension']: getExtension(i + 1), - ['firefox---extension']: getExtension(i + 2), -})); +const basicDatatable = { + columns: ['count', 'extension'].map((id) => ({ id } as DatatableColumn)), + rows: Array.from({ length: 10 }).map((_, i) => ({ + count: i, + extension: getNextExtension(), + })) as DatatableRow[], +}; describe('getColorCategories', () => { - it('should return all categories from datatable rows', () => { - expect(getColorCategories(basicDatatableRows, 'extension')).toEqual([ - 'gz', - 'css', - '', - 'rpm', - 'deb', - 'zip', - 'null', - ]); + it('should return no categories when accessor is undefined', () => { + expect(getColorCategories(basicDatatable.rows)).toEqual([]); }); - it('should exclude selected categories from datatable rows', () => { - expect(getColorCategories(basicDatatableRows, 'extension', false, ['', null])).toEqual([ - 'gz', - 'css', - 'rpm', - 'deb', - 'zip', - ]); + it('should return no categories when accessor is not found', () => { + expect(getColorCategories(basicDatatable.rows, 'N/A')).toEqual([]); + }); + + it('should return no categories when no rows are defined', () => { + expect(getColorCategories(undefined, 'extension')).toEqual([]); }); - it('should return categories across all transpose columns of datatable rows', () => { - expect(getColorCategories(isTransposedDatatableRows, 'extension', true)).toEqual([ + it('should return all categories from non-transpose datatable', () => { + expect(getColorCategories(basicDatatable.rows, 'extension')).toEqual([ 'gz', 'css', '', @@ -60,8 +49,8 @@ describe('getColorCategories', () => { ]); }); - it('should exclude selected categories across all transpose columns of datatable rows', () => { - expect(getColorCategories(isTransposedDatatableRows, 'extension', true, ['', null])).toEqual([ + it('should exclude selected categories from non-transpose datatable', () => { + expect(getColorCategories(basicDatatable.rows, 'extension', ['', null])).toEqual([ 'gz', 'css', 'rpm', diff --git a/src/platform/packages/shared/chart-expressions-common/color_categories.ts b/src/platform/packages/shared/chart-expressions-common/color_categories.ts index 1f8ed39e7dae0..d1ee8a2514789 100644 --- a/src/platform/packages/shared/chart-expressions-common/color_categories.ts +++ b/src/platform/packages/shared/chart-expressions-common/color_categories.ts @@ -13,33 +13,27 @@ import { isMultiFieldKey } from '@kbn/data-plugin/common'; /** * Get the stringified version of all the categories that needs to be colored in the chart. * Multifield keys will return as array of string and simple fields (numeric, string) will be returned as a plain unformatted string. + * + * Note: This does **NOT** support transposed columns */ export function getColorCategories( - rows: DatatableRow[], + rows: DatatableRow[] = [], accessor?: string, - isTransposed?: boolean, exclude?: any[] ): Array { - const ids = isTransposed - ? Object.keys(rows[0]).filter((key) => accessor && key.endsWith(accessor)) - : accessor - ? [accessor] - : []; + if (!accessor) return []; return rows - .flatMap((r) => - ids - .map((id) => r[id]) - .filter((v) => !(v === undefined || exclude?.includes(v))) - .map((v) => { - // The categories needs to be stringified in their unformatted version. - // We can't distinguish between a number and a string from a text input and the match should - // work with both numeric field values and string values. - const key = (isMultiFieldKey(v) ? v.keys : [v]).map(String); - const stringifiedKeys = key.join(','); - return { key, stringifiedKeys }; - }) - ) + .filter(({ [accessor]: v }) => !(v === undefined || exclude?.includes(v))) + .map((r) => { + const v = r[accessor]; + // The categories needs to be stringified in their unformatted version. + // We can't distinguish between a number and a string from a text input and the match should + // work with both numeric field values and string values. + const key = (isMultiFieldKey(v) ? v.keys : [v]).map(String); + const stringifiedKeys = key.join(','); + return { key, stringifiedKeys }; + }) .reduce<{ keys: Set; categories: Array }>( (acc, { key, stringifiedKeys }) => { if (!acc.keys.has(stringifiedKeys)) { diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/index.ts b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/index.ts index 6ede0a0b63ea6..79f43a313f38b 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/index.ts +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/index.ts @@ -10,3 +10,4 @@ export { generateMockData } from './data'; export { getRenderCellValueMock } from './render_cell_value_mock'; export { DataGridWithInTableSearchExample } from './data_grid_example'; +export { MockContext, useMockContextValue } from './mock_context'; diff --git a/src/platform/packages/private/serverless/project_switcher/src/loader.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/mock_context.ts similarity index 57% rename from src/platform/packages/private/serverless/project_switcher/src/loader.tsx rename to src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/mock_context.ts index 5f3e513dc4b37..a7e4ff331057d 100644 --- a/src/platform/packages/private/serverless/project_switcher/src/loader.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/mock_context.ts @@ -9,14 +9,10 @@ import React from 'react'; -import { Logo, type Props } from './logo'; +interface MockContextValue { + mockContextValue?: string; +} -export const Loader = (props: Props) => ( -
-
- -
Loading Project
-
-
-
-); +export const MockContext = React.createContext({}); + +export const useMockContextValue = () => React.useContext(MockContext).mockContextValue; diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/render_cell_value_mock.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/render_cell_value_mock.tsx index 0016fce431b7d..35c9634dcbc7b 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/render_cell_value_mock.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__mocks__/render_cell_value_mock.tsx @@ -9,18 +9,28 @@ import React from 'react'; import type { EuiDataGridCellValueElementProps } from '@elastic/eui'; +import { useMockContextValue } from './mock_context'; export function getRenderCellValueMock(testData: string[][]) { return function OriginalRenderCellValue({ colIndex, rowIndex, }: EuiDataGridCellValueElementProps) { + const mockContextValue = useMockContextValue(); const cellValue = testData[rowIndex][colIndex]; if (!cellValue) { throw new Error('Testing unexpected errors'); } - return
{cellValue}
; + return ( +
+ {cellValue} + { + // testing that it can access the parent context value + mockContextValue ? {mockContextValue} : null + } +
+ ); }; } diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__snapshots__/in_table_search_input.test.tsx.snap b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__snapshots__/in_table_search_input.test.tsx.snap index 3ef95996170af..1f13d9b719b7e 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__snapshots__/in_table_search_input.test.tsx.snap +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/__snapshots__/in_table_search_input.test.tsx.snap @@ -44,7 +44,6 @@ exports[`InTableSearchInput renders input 1`] = ` class="euiText emotion-euiText-s-euiTextColor-subdued" > 5/10 -  
0/0 -  
{ + const testData = [ + ['aaaa', '100'], + ['bbb', 'abb'], + ['abc', 'aaac'], + ]; + + const testData2 = [ + ['bb', 'cc'], + ['bc', 'caa'], + ]; + + const visibleColumns = Array.from({ length: 2 }, (_, i) => `column${i}`); + const getColumnIndexFromId = (columnId: string) => parseInt(columnId.replace('column', ''), 10); + + it('should update correctly when deps change', async () => { + const initialProps: InTableSearchControlProps = { + inTableSearchTerm: 'a', + pageSize: 10, + visibleColumns, + rows: testData, + renderCellValue: jest.fn( + wrapRenderCellValueWithInTableSearchSupport(getRenderCellValueMock(testData)) + ), + getColumnIndexFromId: jest.fn(getColumnIndexFromId), + scrollToCell: jest.fn(), + shouldOverrideCmdF: jest.fn(), + onChange: jest.fn(), + onChangeCss: jest.fn(), + onChangeToExpectedPage: jest.fn(), + }; + + const { rerender } = render(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/9'); + }); + + await waitFor(() => { + expect(initialProps.onChangeToExpectedPage).toHaveBeenCalledWith(0); + }); + + expect(initialProps.getColumnIndexFromId).toHaveBeenCalledWith('column0'); + expect(initialProps.scrollToCell).toHaveBeenCalledWith({ + align: 'center', + columnIndex: 0, + rowIndex: 0, + }); + expect(initialProps.onChange).not.toHaveBeenCalled(); + expect(initialProps.onChangeCss).toHaveBeenCalledWith( + expect.objectContaining({ + styles: expect.stringContaining( + "[data-gridcell-row-index='0'][data-gridcell-column-id='column0']" + ), + }) + ); + expect(initialProps.onChangeCss).toHaveBeenLastCalledWith( + expect.objectContaining({ + styles: expect.stringContaining( + `.${HIGHLIGHT_CLASS_NAME}[${CELL_MATCH_INDEX_ATTRIBUTE}='0']` + ), + }) + ); + + rerender( + + ); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/2'); + }); + + await waitFor(() => { + expect(initialProps.onChangeToExpectedPage).toHaveBeenNthCalledWith(2, 0); + }); + + expect(initialProps.getColumnIndexFromId).toHaveBeenLastCalledWith('column1'); + expect(initialProps.scrollToCell).toHaveBeenLastCalledWith({ + align: 'center', + columnIndex: 1, + rowIndex: 1, + }); + expect(initialProps.onChange).not.toHaveBeenCalled(); + expect(initialProps.onChangeCss).toHaveBeenLastCalledWith( + expect.objectContaining({ + styles: expect.stringContaining( + "[data-gridcell-row-index='1'][data-gridcell-column-id='column1']" + ), + }) + ); + expect(initialProps.onChangeCss).toHaveBeenLastCalledWith( + expect.objectContaining({ + styles: expect.stringContaining( + `.${HIGHLIGHT_CLASS_NAME}[${CELL_MATCH_INDEX_ATTRIBUTE}='0']` + ), + }) + ); + }); + + it('should update correctly when search term changes', async () => { + const initialProps: InTableSearchControlProps = { + inTableSearchTerm: 'aa', + pageSize: null, + visibleColumns, + rows: testData, + renderCellValue: jest.fn( + wrapRenderCellValueWithInTableSearchSupport(getRenderCellValueMock(testData)) + ), + getColumnIndexFromId: jest.fn(getColumnIndexFromId), + scrollToCell: jest.fn(), + shouldOverrideCmdF: jest.fn(), + onChange: jest.fn(), + onChangeCss: jest.fn(), + onChangeToExpectedPage: jest.fn(), + }; + + const { rerender } = render(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/3'); + }); + + rerender(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/6'); + }); + }); + + it('should change pages correctly', async () => { + const initialProps: InTableSearchControlProps = { + inTableSearchTerm: 'abc', + pageSize: 2, + visibleColumns, + rows: testData, + renderCellValue: jest.fn( + wrapRenderCellValueWithInTableSearchSupport(getRenderCellValueMock(testData)) + ), + getColumnIndexFromId: jest.fn(getColumnIndexFromId), + scrollToCell: jest.fn(), + shouldOverrideCmdF: jest.fn(), + onChange: jest.fn(), + onChangeCss: jest.fn(), + onChangeToExpectedPage: jest.fn(), + }; + + const { rerender } = render(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/1'); + }); + + expect(initialProps.onChangeToExpectedPage).toHaveBeenCalledWith(1); + + rerender(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/2'); + }); + + expect(initialProps.onChangeToExpectedPage).toHaveBeenNthCalledWith(2, 2); + + rerender(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/1'); + }); + + expect(initialProps.onChangeToExpectedPage).toHaveBeenNthCalledWith(3, 0); + + rerender(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('0/0'); + }); + + rerender(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/1'); + }); + + expect(initialProps.onChangeToExpectedPage).toHaveBeenCalledTimes(3); + }); + + it('should highlight the active match correctly', async () => { + const initialProps: InTableSearchControlProps = { + inTableSearchTerm: 'aa', + pageSize: 2, + visibleColumns, + rows: testData, + renderCellValue: jest.fn( + wrapRenderCellValueWithInTableSearchSupport(getRenderCellValueMock(testData)) + ), + getColumnIndexFromId: jest.fn(getColumnIndexFromId), + scrollToCell: jest.fn(), + shouldOverrideCmdF: jest.fn(), + onChange: jest.fn(), + onChangeCss: jest.fn(), + onChangeToExpectedPage: jest.fn(), + }; + + render(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/3'); + }); + + await waitFor(() => { + expect(initialProps.onChangeToExpectedPage).toHaveBeenCalledWith(0); + }); + + expect(initialProps.scrollToCell).toHaveBeenCalledWith({ + align: 'center', + columnIndex: 0, + rowIndex: 0, + }); + expect(initialProps.onChangeCss).toHaveBeenCalledWith( + expect.objectContaining({ + styles: expect.stringContaining( + "[data-gridcell-row-index='0'][data-gridcell-column-id='column0']" + ), + }) + ); + expect(initialProps.onChangeCss).toHaveBeenLastCalledWith( + expect.objectContaining({ + styles: expect.stringContaining( + `.${HIGHLIGHT_CLASS_NAME}[${CELL_MATCH_INDEX_ATTRIBUTE}='0']` + ), + }) + ); + + screen.getByTestId(BUTTON_NEXT_TEST_SUBJ).click(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('2/3'); + }); + + await waitFor(() => { + expect(initialProps.onChangeToExpectedPage).toHaveBeenNthCalledWith(2, 0); + }); + + expect(initialProps.scrollToCell).toHaveBeenNthCalledWith(2, { + align: 'center', + columnIndex: 0, + rowIndex: 0, + }); + expect(initialProps.onChangeCss).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + styles: expect.stringContaining( + "[data-gridcell-row-index='0'][data-gridcell-column-id='column0']" + ), + }) + ); + expect(initialProps.onChangeCss).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + styles: expect.stringContaining( + `.${HIGHLIGHT_CLASS_NAME}[${CELL_MATCH_INDEX_ATTRIBUTE}='1']` + ), + }) + ); + + screen.getByTestId(BUTTON_NEXT_TEST_SUBJ).click(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('3/3'); + }); + + await waitFor(() => { + expect(initialProps.onChangeToExpectedPage).toHaveBeenNthCalledWith(3, 1); + }); + + expect(initialProps.scrollToCell).toHaveBeenNthCalledWith(3, { + align: 'center', + columnIndex: 1, + rowIndex: 0, + }); + expect(initialProps.onChangeCss).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + styles: expect.stringContaining( + "[data-gridcell-row-index='2'][data-gridcell-column-id='column1']" + ), + }) + ); + expect(initialProps.onChangeCss).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + styles: expect.stringContaining( + `.${HIGHLIGHT_CLASS_NAME}[${CELL_MATCH_INDEX_ATTRIBUTE}='0']` + ), + }) + ); + }); + + it('should handle timeouts', async () => { + const initialProps: InTableSearchControlProps = { + inTableSearchTerm: 'aa', + pageSize: null, + visibleColumns, + rows: testData, + renderCellValue: jest.fn( + wrapRenderCellValueWithInTableSearchSupport(getRenderCellValueMock(testData)) + ), + getColumnIndexFromId: jest.fn(getColumnIndexFromId), + scrollToCell: jest.fn(), + shouldOverrideCmdF: jest.fn(), + onChange: jest.fn(), + onChangeCss: jest.fn(), + onChangeToExpectedPage: jest.fn(), + }; + + const { rerender } = render(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/3'); + }); + + rerender(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('0/0'); + }); + }); + + it('should handle ignore errors in cells', async () => { + const initialProps: InTableSearchControlProps = { + inTableSearchTerm: 'aa', + pageSize: null, + visibleColumns: [visibleColumns[0]], + rows: testData, + renderCellValue: jest.fn( + wrapRenderCellValueWithInTableSearchSupport(getRenderCellValueMock(testData)) + ), + getColumnIndexFromId: jest.fn(getColumnIndexFromId), + scrollToCell: jest.fn(), + shouldOverrideCmdF: jest.fn(), + onChange: jest.fn(), + onChangeCss: jest.fn(), + onChangeToExpectedPage: jest.fn(), + }; + + const { rerender } = render(); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/2'); + }); + + rerender( + + ); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/3'); + }); + }); +}); diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_control.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_control.tsx index cab302a30c7a9..0da5b969928e0 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_control.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_control.tsx @@ -9,6 +9,7 @@ import React, { useCallback, useState, useEffect, useRef } from 'react'; import { EuiButtonIcon, EuiToolTip, useEuiTheme } from '@elastic/eui'; +import useEvent from 'react-use/lib/useEvent'; import { i18n } from '@kbn/i18n'; import { css, type SerializedStyles } from '@emotion/react'; import { useFindMatches } from './matches/use_find_matches'; @@ -33,7 +34,7 @@ const innerCss = css` } .euiFormControlLayout__append { - padding-inline-end: 0 !important; + padding-inline: 0 !important; background: none; } @@ -69,8 +70,9 @@ export const InTableSearchControl: React.FC = ({ }) => { const { euiTheme } = useEuiTheme(); const containerRef = useRef(null); + const buttonRef = useRef(null); const shouldReturnFocusToButtonRef = useRef(false); - const [isInputVisible, setIsInputVisible] = useState(false); + const [isInputVisible, setIsInputVisible] = useState(Boolean(props.inTableSearchTerm)); const onScrollToActiveMatch: UseFindMatchesProps['onScrollToActiveMatch'] = useCallback( ({ rowIndex, columnId, matchIndexWithinCell }) => { @@ -133,8 +135,8 @@ export const InTableSearchControl: React.FC = ({ ); // listens for the cmd+f or ctrl+f keydown event to open the input - useEffect(() => { - const handleGlobalKeyDown = (event: KeyboardEvent) => { + const handleGlobalKeyDown = useCallback( + (event: KeyboardEvent) => { if ( (event.metaKey || event.ctrlKey) && event.key === 'f' && @@ -150,24 +152,17 @@ export const InTableSearchControl: React.FC = ({ ) as HTMLInputElement )?.focus(); } - }; - - document.addEventListener('keydown', handleGlobalKeyDown); + }, + [showInput, shouldOverrideCmdF] + ); - return () => { - document.removeEventListener('keydown', handleGlobalKeyDown); - }; - }, [showInput, shouldOverrideCmdF]); + useEvent('keydown', handleGlobalKeyDown); // returns focus to the button when the input was cancelled by pressing the escape key useEffect(() => { if (shouldReturnFocusToButtonRef.current && !isInputVisible) { shouldReturnFocusToButtonRef.current = false; - ( - containerRef.current?.querySelector( - `[data-test-subj="${BUTTON_TEST_SUBJ}"]` - ) as HTMLButtonElement - )?.focus(); + buttonRef.current?.focus(); } }, [isInputVisible]); @@ -197,6 +192,7 @@ export const InTableSearchControl: React.FC = ({ > {children}
; }; -const getSearchTermRegExp = memoize((searchTerm: string): RegExp => { - return new RegExp(`(${escapeRegExp(searchTerm.trim())})`, 'gi'); -}); +const searchTermRegExpCache = new Map(); + +const getSearchTermRegExp = (searchTerm: string): RegExp => { + if (searchTermRegExpCache.has(searchTerm)) { + return searchTermRegExpCache.get(searchTerm)!; + } + + const searchTermRegExp = new RegExp(`(${escapeRegExp(searchTerm.trim())})`, 'gi'); + searchTermRegExpCache.set(searchTerm, searchTermRegExp); + return searchTermRegExp; +}; + +export const clearSearchTermRegExpCache = () => { + searchTermRegExpCache.clear(); +}; function modifyDOMAndAddSearchHighlights( originalNode: Node, diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_input.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_input.tsx index f189b82526a73..7eb00d751f2a8 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_input.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/in_table_search_input.tsx @@ -108,7 +108,6 @@ export const InTableSearchInput: React.FC = React.memo( {matchesCount && activeMatchPosition ? `${activeMatchPosition}/${matchesCount}` : '0/0'} -  
diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/matches/row_cells_renderer.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/matches/row_cells_renderer.tsx index 9d0ed26db67c9..d15a5b97967df 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/matches/row_cells_renderer.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/matches/row_cells_renderer.tsx @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { useCallback, useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { AllCellsProps, RowMatches } from '../types'; const TIMEOUT_PER_ROW = 2000; // 2 sec per row max @@ -28,14 +28,15 @@ export function RowCellsRenderer({ const matchesCountPerColumnIdRef = useRef>({}); const rowMatchesCountRef = useRef(0); const remainingNumberOfResultsRef = useRef(visibleColumns.length); - const isCompletedRef = useRef(false); + const hasCompletedRef = useRef(false); + const [hasTimedOut, setHasTimedOut] = useState(false); // all cells in the row were processed const onComplete = useCallback(() => { - if (isCompletedRef.current) { + if (hasCompletedRef.current) { return; } - isCompletedRef.current = true; // report only once + hasCompletedRef.current = true; // report only once onRowProcessed({ rowIndex, rowMatchesCount: rowMatchesCountRef.current, @@ -69,7 +70,8 @@ export function RowCellsRenderer({ } timerRef.current = setTimeout(() => { - onCompleteRef.current?.(); + onCompleteRef.current?.(); // at least report back the already collected results + setHasTimedOut(true); }, TIMEOUT_PER_ROW); return () => { @@ -77,7 +79,12 @@ export function RowCellsRenderer({ clearTimeout(timerRef.current); } }; - }, [rowIndex]); + }, [rowIndex, setHasTimedOut]); + + if (hasTimedOut) { + // stop any further processing + return null; + } return ( <> diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.test.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.test.tsx index 2ee72a004ee25..be30c7ccc0ce6 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.test.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.test.tsx @@ -13,6 +13,7 @@ import { DataGridWithInTableSearchExample, generateMockData, getRenderCellValueMock, + MockContext, } from './__mocks__'; import { useDataGridInTableSearch } from './use_data_grid_in_table_search'; import { @@ -135,4 +136,27 @@ describe('useDataGridInTableSearch', () => { ).toBe(true); }); }); + + it('should handle parent contexts correctly', async () => { + render( + + + + ); + + screen.getByTestId(BUTTON_TEST_SUBJ).click(); + + await waitFor(() => { + expect(screen.getByTestId(INPUT_TEST_SUBJ)).toBeInTheDocument(); + }); + + const searchTerm = 'test access'; + const input = screen.getByTestId(INPUT_TEST_SUBJ); + fireEvent.change(input, { target: { value: searchTerm } }); + expect(input).toHaveValue(searchTerm); + + await waitFor(() => { + expect(screen.getByTestId(COUNTER_TEST_SUBJ)).toHaveTextContent('1/200'); + }); + }); }); diff --git a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.tsx b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.tsx index 13f7a138e8124..c0c35b75b4862 100644 --- a/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.tsx +++ b/src/platform/packages/shared/kbn-data-grid-in-table-search/src/use_data_grid_in_table_search.tsx @@ -7,12 +7,13 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { useMemo, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import type { SerializedStyles } from '@emotion/react'; import type { EuiDataGridProps, EuiDataGridRefProps } from '@elastic/eui'; import { InTableSearchControl, InTableSearchControlProps } from './in_table_search_control'; import { RenderCellValueWrapper } from './types'; import { wrapRenderCellValueWithInTableSearchSupport } from './wrap_render_cell_value'; +import { clearSearchTermRegExpCache } from './in_table_search_highlights_wrapper'; export interface UseDataGridInTableSearchProps extends Pick { @@ -87,7 +88,13 @@ export const useDataGridInTableSearch = ( } return dataGridWrapper.contains?.(element) ?? false; }} - onChange={(searchTerm) => setInTableSearchState({ inTableSearchTerm: searchTerm || '' })} + onChange={(searchTerm) => { + const nextSearchTerm = searchTerm || ''; + setInTableSearchState({ inTableSearchTerm: nextSearchTerm }); + if (!nextSearchTerm) { + clearSearchTermRegExpCache(); + } + }} onChangeCss={(styles) => setInTableSearchState((prevState) => ({ ...prevState, inTableSearchTermCss: styles })) } @@ -123,6 +130,12 @@ export const useDataGridInTableSearch = ( }; }, [cellContext, inTableSearchTerm]); + useEffect(() => { + return () => { + clearSearchTermRegExpCache(); + }; + }, []); + return useMemo( () => ({ inTableSearchTermCss, diff --git a/src/platform/packages/shared/kbn-esql-ast/index.ts b/src/platform/packages/shared/kbn-esql-ast/index.ts index 8cbf45a04e560..e98e025fef3b9 100644 --- a/src/platform/packages/shared/kbn-esql-ast/index.ts +++ b/src/platform/packages/shared/kbn-esql-ast/index.ts @@ -12,6 +12,7 @@ export type { ESQLAstItem, ESQLAstCommand, ESQLAstMetricsCommand, + ESQLAstJoinCommand, ESQLCommand, ESQLCommandOption, ESQLCommandMode, diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.g4 b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.g4 index 4eac61772ab57..debe6e10c9fb4 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.g4 +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.g4 @@ -75,6 +75,7 @@ SHOW : 'show' -> pushMode(SHOW_MODE); SORT : 'sort' -> pushMode(EXPRESSION_MODE); STATS : 'stats' -> pushMode(EXPRESSION_MODE); WHERE : 'where' -> pushMode(EXPRESSION_MODE); +JOIN_LOOKUP : 'lookup' -> pushMode(JOIN_MODE); // // in development // @@ -90,11 +91,9 @@ DEV_INLINESTATS : {this.isDevVersion()}? 'inlinestats' -> pushMode(EXPRESSION_ DEV_LOOKUP : {this.isDevVersion()}? 'lookup_🐔' -> pushMode(LOOKUP_MODE); DEV_METRICS : {this.isDevVersion()}? 'metrics' -> pushMode(METRICS_MODE); // list of all JOIN commands -DEV_JOIN : {this.isDevVersion()}? 'join' -> pushMode(JOIN_MODE); DEV_JOIN_FULL : {this.isDevVersion()}? 'full' -> pushMode(JOIN_MODE); DEV_JOIN_LEFT : {this.isDevVersion()}? 'left' -> pushMode(JOIN_MODE); DEV_JOIN_RIGHT : {this.isDevVersion()}? 'right' -> pushMode(JOIN_MODE); -DEV_JOIN_LOOKUP : {this.isDevVersion()}? 'lookup' -> pushMode(JOIN_MODE); // @@ -218,8 +217,8 @@ ASTERISK : '*'; SLASH : '/'; PERCENT : '%'; -LEFT_BRACES : {this.isDevVersion()}? '{'; -RIGHT_BRACES : {this.isDevVersion()}? '}'; +LEFT_BRACES : '{'; +RIGHT_BRACES : '}'; NESTED_WHERE : WHERE -> type(WHERE); @@ -558,7 +557,7 @@ LOOKUP_FIELD_WS // mode JOIN_MODE; JOIN_PIPE : PIPE -> type(PIPE), popMode; -JOIN_JOIN : DEV_JOIN -> type(DEV_JOIN); +JOIN : 'join'; JOIN_AS : AS -> type(AS); JOIN_ON : ON -> type(ON), popMode, pushMode(EXPRESSION_MODE); USING : 'USING' -> popMode, pushMode(EXPRESSION_MODE); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.interp b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.interp index 92274ebe15513..f0df3817ac658 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.interp +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.interp @@ -16,8 +16,7 @@ null 'sort' 'stats' 'where' -null -null +'lookup' null null null @@ -68,8 +67,8 @@ null '*' '/' '%' -null -null +'{' +'}' null null ']' @@ -120,6 +119,7 @@ null null null null +'join' 'USING' null null @@ -149,14 +149,13 @@ SHOW SORT STATS WHERE +JOIN_LOOKUP DEV_INLINESTATS DEV_LOOKUP DEV_METRICS -DEV_JOIN DEV_JOIN_FULL DEV_JOIN_LEFT DEV_JOIN_RIGHT -DEV_JOIN_LOOKUP UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT @@ -253,6 +252,7 @@ LOOKUP_WS LOOKUP_FIELD_LINE_COMMENT LOOKUP_FIELD_MULTILINE_COMMENT LOOKUP_FIELD_WS +JOIN USING JOIN_LINE_COMMENT JOIN_MULTILINE_COMMENT @@ -281,14 +281,13 @@ SHOW SORT STATS WHERE +JOIN_LOOKUP DEV_INLINESTATS DEV_LOOKUP DEV_METRICS -DEV_JOIN DEV_JOIN_FULL DEV_JOIN_LEFT DEV_JOIN_RIGHT -DEV_JOIN_LOOKUP UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT @@ -456,7 +455,7 @@ LOOKUP_FIELD_LINE_COMMENT LOOKUP_FIELD_MULTILINE_COMMENT LOOKUP_FIELD_WS JOIN_PIPE -JOIN_JOIN +JOIN JOIN_AS JOIN_ON USING @@ -507,4 +506,4 @@ METRICS_MODE CLOSING_METRICS_MODE atn: -[4, 0, 130, 1629, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, 2, 202, 7, 202, 2, 203, 7, 203, 2, 204, 7, 204, 2, 205, 7, 205, 2, 206, 7, 206, 2, 207, 7, 207, 2, 208, 7, 208, 2, 209, 7, 209, 2, 210, 7, 210, 2, 211, 7, 211, 2, 212, 7, 212, 2, 213, 7, 213, 2, 214, 7, 214, 2, 215, 7, 215, 2, 216, 7, 216, 2, 217, 7, 217, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 23, 1, 24, 4, 24, 664, 8, 24, 11, 24, 12, 24, 665, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 5, 25, 674, 8, 25, 10, 25, 12, 25, 677, 9, 25, 1, 25, 3, 25, 680, 8, 25, 1, 25, 3, 25, 683, 8, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 5, 26, 692, 8, 26, 10, 26, 12, 26, 695, 9, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 4, 27, 703, 8, 27, 11, 27, 12, 27, 704, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 1, 33, 3, 33, 724, 8, 33, 1, 33, 4, 33, 727, 8, 33, 11, 33, 12, 33, 728, 1, 34, 1, 34, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 3, 36, 738, 8, 36, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 3, 38, 745, 8, 38, 1, 39, 1, 39, 1, 39, 5, 39, 750, 8, 39, 10, 39, 12, 39, 753, 9, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 5, 39, 761, 8, 39, 10, 39, 12, 39, 764, 9, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 3, 39, 771, 8, 39, 1, 39, 3, 39, 774, 8, 39, 3, 39, 776, 8, 39, 1, 40, 4, 40, 779, 8, 40, 11, 40, 12, 40, 780, 1, 41, 4, 41, 784, 8, 41, 11, 41, 12, 41, 785, 1, 41, 1, 41, 5, 41, 790, 8, 41, 10, 41, 12, 41, 793, 9, 41, 1, 41, 1, 41, 4, 41, 797, 8, 41, 11, 41, 12, 41, 798, 1, 41, 4, 41, 802, 8, 41, 11, 41, 12, 41, 803, 1, 41, 1, 41, 5, 41, 808, 8, 41, 10, 41, 12, 41, 811, 9, 41, 3, 41, 813, 8, 41, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 819, 8, 41, 11, 41, 12, 41, 820, 1, 41, 1, 41, 3, 41, 825, 8, 41, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 3, 81, 959, 8, 81, 1, 81, 5, 81, 962, 8, 81, 10, 81, 12, 81, 965, 9, 81, 1, 81, 1, 81, 4, 81, 969, 8, 81, 11, 81, 12, 81, 970, 3, 81, 973, 8, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 5, 84, 987, 8, 84, 10, 84, 12, 84, 990, 9, 84, 1, 84, 1, 84, 3, 84, 994, 8, 84, 1, 84, 4, 84, 997, 8, 84, 11, 84, 12, 84, 998, 3, 84, 1001, 8, 84, 1, 85, 1, 85, 4, 85, 1005, 8, 85, 11, 85, 12, 85, 1006, 1, 85, 1, 85, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 3, 102, 1084, 8, 102, 1, 103, 4, 103, 1087, 8, 103, 11, 103, 12, 103, 1088, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 3, 114, 1138, 8, 114, 1, 115, 1, 115, 3, 115, 1142, 8, 115, 1, 115, 5, 115, 1145, 8, 115, 10, 115, 12, 115, 1148, 9, 115, 1, 115, 1, 115, 3, 115, 1152, 8, 115, 1, 115, 4, 115, 1155, 8, 115, 11, 115, 12, 115, 1156, 3, 115, 1159, 8, 115, 1, 116, 1, 116, 4, 116, 1163, 8, 116, 11, 116, 12, 116, 1164, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 136, 4, 136, 1250, 8, 136, 11, 136, 12, 136, 1251, 1, 136, 1, 136, 3, 136, 1256, 8, 136, 1, 136, 4, 136, 1259, 8, 136, 11, 136, 12, 136, 1260, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 169, 1, 169, 1, 169, 1, 169, 1, 169, 4, 169, 1406, 8, 169, 11, 169, 12, 169, 1407, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 198, 1, 198, 1, 198, 1, 198, 1, 199, 1, 199, 1, 199, 1, 199, 1, 200, 1, 200, 1, 200, 1, 200, 1, 201, 1, 201, 1, 201, 1, 201, 1, 202, 1, 202, 1, 202, 1, 202, 1, 203, 1, 203, 1, 203, 1, 203, 1, 203, 1, 204, 1, 204, 1, 204, 1, 204, 1, 204, 1, 204, 1, 205, 1, 205, 1, 205, 1, 205, 1, 205, 1, 205, 1, 206, 1, 206, 1, 206, 1, 206, 1, 207, 1, 207, 1, 207, 1, 207, 1, 208, 1, 208, 1, 208, 1, 208, 1, 209, 1, 209, 1, 209, 1, 209, 1, 209, 1, 209, 1, 210, 1, 210, 1, 210, 1, 210, 1, 210, 1, 210, 1, 211, 1, 211, 1, 211, 1, 211, 1, 212, 1, 212, 1, 212, 1, 212, 1, 213, 1, 213, 1, 213, 1, 213, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 1, 215, 1, 215, 1, 215, 1, 215, 1, 215, 1, 215, 1, 216, 1, 216, 1, 216, 1, 216, 1, 216, 1, 216, 1, 217, 1, 217, 1, 217, 1, 217, 1, 217, 2, 693, 762, 0, 218, 16, 1, 18, 2, 20, 3, 22, 4, 24, 5, 26, 6, 28, 7, 30, 8, 32, 9, 34, 10, 36, 11, 38, 12, 40, 13, 42, 14, 44, 15, 46, 16, 48, 17, 50, 18, 52, 19, 54, 20, 56, 21, 58, 22, 60, 23, 62, 24, 64, 25, 66, 26, 68, 27, 70, 28, 72, 29, 74, 0, 76, 0, 78, 0, 80, 0, 82, 0, 84, 0, 86, 0, 88, 0, 90, 0, 92, 0, 94, 30, 96, 31, 98, 32, 100, 33, 102, 34, 104, 35, 106, 36, 108, 37, 110, 38, 112, 39, 114, 40, 116, 41, 118, 42, 120, 43, 122, 44, 124, 45, 126, 46, 128, 47, 130, 48, 132, 49, 134, 50, 136, 51, 138, 52, 140, 53, 142, 54, 144, 55, 146, 56, 148, 57, 150, 58, 152, 59, 154, 60, 156, 61, 158, 62, 160, 63, 162, 64, 164, 65, 166, 66, 168, 67, 170, 68, 172, 69, 174, 70, 176, 0, 178, 71, 180, 72, 182, 73, 184, 74, 186, 0, 188, 75, 190, 76, 192, 77, 194, 78, 196, 0, 198, 0, 200, 79, 202, 80, 204, 81, 206, 0, 208, 0, 210, 0, 212, 0, 214, 0, 216, 0, 218, 82, 220, 0, 222, 83, 224, 0, 226, 0, 228, 84, 230, 85, 232, 86, 234, 0, 236, 0, 238, 0, 240, 0, 242, 0, 244, 0, 246, 0, 248, 87, 250, 88, 252, 89, 254, 90, 256, 0, 258, 0, 260, 0, 262, 0, 264, 0, 266, 0, 268, 91, 270, 0, 272, 92, 274, 93, 276, 94, 278, 0, 280, 0, 282, 95, 284, 96, 286, 0, 288, 97, 290, 0, 292, 98, 294, 99, 296, 100, 298, 0, 300, 0, 302, 0, 304, 0, 306, 0, 308, 0, 310, 0, 312, 0, 314, 0, 316, 101, 318, 102, 320, 103, 322, 0, 324, 0, 326, 0, 328, 0, 330, 0, 332, 0, 334, 104, 336, 105, 338, 106, 340, 0, 342, 107, 344, 108, 346, 109, 348, 110, 350, 0, 352, 0, 354, 111, 356, 112, 358, 113, 360, 114, 362, 0, 364, 0, 366, 0, 368, 0, 370, 0, 372, 0, 374, 0, 376, 115, 378, 116, 380, 117, 382, 0, 384, 0, 386, 0, 388, 0, 390, 118, 392, 119, 394, 120, 396, 0, 398, 0, 400, 0, 402, 0, 404, 121, 406, 0, 408, 0, 410, 0, 412, 0, 414, 0, 416, 122, 418, 123, 420, 124, 422, 0, 424, 0, 426, 0, 428, 125, 430, 126, 432, 127, 434, 0, 436, 0, 438, 128, 440, 129, 442, 130, 444, 0, 446, 0, 448, 0, 450, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 36, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 2, 0, 74, 74, 106, 106, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 1656, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 0, 56, 1, 0, 0, 0, 0, 58, 1, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 68, 1, 0, 0, 0, 0, 70, 1, 0, 0, 0, 1, 72, 1, 0, 0, 0, 1, 94, 1, 0, 0, 0, 1, 96, 1, 0, 0, 0, 1, 98, 1, 0, 0, 0, 1, 100, 1, 0, 0, 0, 1, 102, 1, 0, 0, 0, 1, 104, 1, 0, 0, 0, 1, 106, 1, 0, 0, 0, 1, 108, 1, 0, 0, 0, 1, 110, 1, 0, 0, 0, 1, 112, 1, 0, 0, 0, 1, 114, 1, 0, 0, 0, 1, 116, 1, 0, 0, 0, 1, 118, 1, 0, 0, 0, 1, 120, 1, 0, 0, 0, 1, 122, 1, 0, 0, 0, 1, 124, 1, 0, 0, 0, 1, 126, 1, 0, 0, 0, 1, 128, 1, 0, 0, 0, 1, 130, 1, 0, 0, 0, 1, 132, 1, 0, 0, 0, 1, 134, 1, 0, 0, 0, 1, 136, 1, 0, 0, 0, 1, 138, 1, 0, 0, 0, 1, 140, 1, 0, 0, 0, 1, 142, 1, 0, 0, 0, 1, 144, 1, 0, 0, 0, 1, 146, 1, 0, 0, 0, 1, 148, 1, 0, 0, 0, 1, 150, 1, 0, 0, 0, 1, 152, 1, 0, 0, 0, 1, 154, 1, 0, 0, 0, 1, 156, 1, 0, 0, 0, 1, 158, 1, 0, 0, 0, 1, 160, 1, 0, 0, 0, 1, 162, 1, 0, 0, 0, 1, 164, 1, 0, 0, 0, 1, 166, 1, 0, 0, 0, 1, 168, 1, 0, 0, 0, 1, 170, 1, 0, 0, 0, 1, 172, 1, 0, 0, 0, 1, 174, 1, 0, 0, 0, 1, 176, 1, 0, 0, 0, 1, 178, 1, 0, 0, 0, 1, 180, 1, 0, 0, 0, 1, 182, 1, 0, 0, 0, 1, 184, 1, 0, 0, 0, 1, 188, 1, 0, 0, 0, 1, 190, 1, 0, 0, 0, 1, 192, 1, 0, 0, 0, 1, 194, 1, 0, 0, 0, 2, 196, 1, 0, 0, 0, 2, 198, 1, 0, 0, 0, 2, 200, 1, 0, 0, 0, 2, 202, 1, 0, 0, 0, 2, 204, 1, 0, 0, 0, 3, 206, 1, 0, 0, 0, 3, 208, 1, 0, 0, 0, 3, 210, 1, 0, 0, 0, 3, 212, 1, 0, 0, 0, 3, 214, 1, 0, 0, 0, 3, 216, 1, 0, 0, 0, 3, 218, 1, 0, 0, 0, 3, 222, 1, 0, 0, 0, 3, 224, 1, 0, 0, 0, 3, 226, 1, 0, 0, 0, 3, 228, 1, 0, 0, 0, 3, 230, 1, 0, 0, 0, 3, 232, 1, 0, 0, 0, 4, 234, 1, 0, 0, 0, 4, 236, 1, 0, 0, 0, 4, 238, 1, 0, 0, 0, 4, 240, 1, 0, 0, 0, 4, 242, 1, 0, 0, 0, 4, 248, 1, 0, 0, 0, 4, 250, 1, 0, 0, 0, 4, 252, 1, 0, 0, 0, 4, 254, 1, 0, 0, 0, 5, 256, 1, 0, 0, 0, 5, 258, 1, 0, 0, 0, 5, 260, 1, 0, 0, 0, 5, 262, 1, 0, 0, 0, 5, 264, 1, 0, 0, 0, 5, 266, 1, 0, 0, 0, 5, 268, 1, 0, 0, 0, 5, 270, 1, 0, 0, 0, 5, 272, 1, 0, 0, 0, 5, 274, 1, 0, 0, 0, 5, 276, 1, 0, 0, 0, 6, 278, 1, 0, 0, 0, 6, 280, 1, 0, 0, 0, 6, 282, 1, 0, 0, 0, 6, 284, 1, 0, 0, 0, 6, 288, 1, 0, 0, 0, 6, 290, 1, 0, 0, 0, 6, 292, 1, 0, 0, 0, 6, 294, 1, 0, 0, 0, 6, 296, 1, 0, 0, 0, 7, 298, 1, 0, 0, 0, 7, 300, 1, 0, 0, 0, 7, 302, 1, 0, 0, 0, 7, 304, 1, 0, 0, 0, 7, 306, 1, 0, 0, 0, 7, 308, 1, 0, 0, 0, 7, 310, 1, 0, 0, 0, 7, 312, 1, 0, 0, 0, 7, 314, 1, 0, 0, 0, 7, 316, 1, 0, 0, 0, 7, 318, 1, 0, 0, 0, 7, 320, 1, 0, 0, 0, 8, 322, 1, 0, 0, 0, 8, 324, 1, 0, 0, 0, 8, 326, 1, 0, 0, 0, 8, 328, 1, 0, 0, 0, 8, 330, 1, 0, 0, 0, 8, 332, 1, 0, 0, 0, 8, 334, 1, 0, 0, 0, 8, 336, 1, 0, 0, 0, 8, 338, 1, 0, 0, 0, 9, 340, 1, 0, 0, 0, 9, 342, 1, 0, 0, 0, 9, 344, 1, 0, 0, 0, 9, 346, 1, 0, 0, 0, 9, 348, 1, 0, 0, 0, 10, 350, 1, 0, 0, 0, 10, 352, 1, 0, 0, 0, 10, 354, 1, 0, 0, 0, 10, 356, 1, 0, 0, 0, 10, 358, 1, 0, 0, 0, 10, 360, 1, 0, 0, 0, 11, 362, 1, 0, 0, 0, 11, 364, 1, 0, 0, 0, 11, 366, 1, 0, 0, 0, 11, 368, 1, 0, 0, 0, 11, 370, 1, 0, 0, 0, 11, 372, 1, 0, 0, 0, 11, 374, 1, 0, 0, 0, 11, 376, 1, 0, 0, 0, 11, 378, 1, 0, 0, 0, 11, 380, 1, 0, 0, 0, 12, 382, 1, 0, 0, 0, 12, 384, 1, 0, 0, 0, 12, 386, 1, 0, 0, 0, 12, 388, 1, 0, 0, 0, 12, 390, 1, 0, 0, 0, 12, 392, 1, 0, 0, 0, 12, 394, 1, 0, 0, 0, 13, 396, 1, 0, 0, 0, 13, 398, 1, 0, 0, 0, 13, 400, 1, 0, 0, 0, 13, 402, 1, 0, 0, 0, 13, 404, 1, 0, 0, 0, 13, 406, 1, 0, 0, 0, 13, 408, 1, 0, 0, 0, 13, 410, 1, 0, 0, 0, 13, 412, 1, 0, 0, 0, 13, 414, 1, 0, 0, 0, 13, 416, 1, 0, 0, 0, 13, 418, 1, 0, 0, 0, 13, 420, 1, 0, 0, 0, 14, 422, 1, 0, 0, 0, 14, 424, 1, 0, 0, 0, 14, 426, 1, 0, 0, 0, 14, 428, 1, 0, 0, 0, 14, 430, 1, 0, 0, 0, 14, 432, 1, 0, 0, 0, 15, 434, 1, 0, 0, 0, 15, 436, 1, 0, 0, 0, 15, 438, 1, 0, 0, 0, 15, 440, 1, 0, 0, 0, 15, 442, 1, 0, 0, 0, 15, 444, 1, 0, 0, 0, 15, 446, 1, 0, 0, 0, 15, 448, 1, 0, 0, 0, 15, 450, 1, 0, 0, 0, 16, 452, 1, 0, 0, 0, 18, 462, 1, 0, 0, 0, 20, 469, 1, 0, 0, 0, 22, 478, 1, 0, 0, 0, 24, 485, 1, 0, 0, 0, 26, 495, 1, 0, 0, 0, 28, 502, 1, 0, 0, 0, 30, 509, 1, 0, 0, 0, 32, 516, 1, 0, 0, 0, 34, 524, 1, 0, 0, 0, 36, 536, 1, 0, 0, 0, 38, 545, 1, 0, 0, 0, 40, 551, 1, 0, 0, 0, 42, 558, 1, 0, 0, 0, 44, 565, 1, 0, 0, 0, 46, 573, 1, 0, 0, 0, 48, 581, 1, 0, 0, 0, 50, 596, 1, 0, 0, 0, 52, 608, 1, 0, 0, 0, 54, 619, 1, 0, 0, 0, 56, 627, 1, 0, 0, 0, 58, 635, 1, 0, 0, 0, 60, 643, 1, 0, 0, 0, 62, 652, 1, 0, 0, 0, 64, 663, 1, 0, 0, 0, 66, 669, 1, 0, 0, 0, 68, 686, 1, 0, 0, 0, 70, 702, 1, 0, 0, 0, 72, 708, 1, 0, 0, 0, 74, 712, 1, 0, 0, 0, 76, 714, 1, 0, 0, 0, 78, 716, 1, 0, 0, 0, 80, 719, 1, 0, 0, 0, 82, 721, 1, 0, 0, 0, 84, 730, 1, 0, 0, 0, 86, 732, 1, 0, 0, 0, 88, 737, 1, 0, 0, 0, 90, 739, 1, 0, 0, 0, 92, 744, 1, 0, 0, 0, 94, 775, 1, 0, 0, 0, 96, 778, 1, 0, 0, 0, 98, 824, 1, 0, 0, 0, 100, 826, 1, 0, 0, 0, 102, 829, 1, 0, 0, 0, 104, 833, 1, 0, 0, 0, 106, 837, 1, 0, 0, 0, 108, 839, 1, 0, 0, 0, 110, 842, 1, 0, 0, 0, 112, 844, 1, 0, 0, 0, 114, 846, 1, 0, 0, 0, 116, 851, 1, 0, 0, 0, 118, 853, 1, 0, 0, 0, 120, 859, 1, 0, 0, 0, 122, 865, 1, 0, 0, 0, 124, 868, 1, 0, 0, 0, 126, 871, 1, 0, 0, 0, 128, 876, 1, 0, 0, 0, 130, 881, 1, 0, 0, 0, 132, 883, 1, 0, 0, 0, 134, 887, 1, 0, 0, 0, 136, 892, 1, 0, 0, 0, 138, 898, 1, 0, 0, 0, 140, 901, 1, 0, 0, 0, 142, 903, 1, 0, 0, 0, 144, 909, 1, 0, 0, 0, 146, 911, 1, 0, 0, 0, 148, 916, 1, 0, 0, 0, 150, 919, 1, 0, 0, 0, 152, 922, 1, 0, 0, 0, 154, 925, 1, 0, 0, 0, 156, 927, 1, 0, 0, 0, 158, 930, 1, 0, 0, 0, 160, 932, 1, 0, 0, 0, 162, 935, 1, 0, 0, 0, 164, 937, 1, 0, 0, 0, 166, 939, 1, 0, 0, 0, 168, 941, 1, 0, 0, 0, 170, 943, 1, 0, 0, 0, 172, 945, 1, 0, 0, 0, 174, 948, 1, 0, 0, 0, 176, 951, 1, 0, 0, 0, 178, 972, 1, 0, 0, 0, 180, 974, 1, 0, 0, 0, 182, 979, 1, 0, 0, 0, 184, 1000, 1, 0, 0, 0, 186, 1002, 1, 0, 0, 0, 188, 1010, 1, 0, 0, 0, 190, 1012, 1, 0, 0, 0, 192, 1016, 1, 0, 0, 0, 194, 1020, 1, 0, 0, 0, 196, 1024, 1, 0, 0, 0, 198, 1029, 1, 0, 0, 0, 200, 1034, 1, 0, 0, 0, 202, 1038, 1, 0, 0, 0, 204, 1042, 1, 0, 0, 0, 206, 1046, 1, 0, 0, 0, 208, 1051, 1, 0, 0, 0, 210, 1055, 1, 0, 0, 0, 212, 1059, 1, 0, 0, 0, 214, 1063, 1, 0, 0, 0, 216, 1067, 1, 0, 0, 0, 218, 1071, 1, 0, 0, 0, 220, 1083, 1, 0, 0, 0, 222, 1086, 1, 0, 0, 0, 224, 1090, 1, 0, 0, 0, 226, 1094, 1, 0, 0, 0, 228, 1098, 1, 0, 0, 0, 230, 1102, 1, 0, 0, 0, 232, 1106, 1, 0, 0, 0, 234, 1110, 1, 0, 0, 0, 236, 1115, 1, 0, 0, 0, 238, 1119, 1, 0, 0, 0, 240, 1123, 1, 0, 0, 0, 242, 1128, 1, 0, 0, 0, 244, 1137, 1, 0, 0, 0, 246, 1158, 1, 0, 0, 0, 248, 1162, 1, 0, 0, 0, 250, 1166, 1, 0, 0, 0, 252, 1170, 1, 0, 0, 0, 254, 1174, 1, 0, 0, 0, 256, 1178, 1, 0, 0, 0, 258, 1183, 1, 0, 0, 0, 260, 1187, 1, 0, 0, 0, 262, 1191, 1, 0, 0, 0, 264, 1195, 1, 0, 0, 0, 266, 1200, 1, 0, 0, 0, 268, 1205, 1, 0, 0, 0, 270, 1208, 1, 0, 0, 0, 272, 1212, 1, 0, 0, 0, 274, 1216, 1, 0, 0, 0, 276, 1220, 1, 0, 0, 0, 278, 1224, 1, 0, 0, 0, 280, 1229, 1, 0, 0, 0, 282, 1234, 1, 0, 0, 0, 284, 1239, 1, 0, 0, 0, 286, 1246, 1, 0, 0, 0, 288, 1255, 1, 0, 0, 0, 290, 1262, 1, 0, 0, 0, 292, 1266, 1, 0, 0, 0, 294, 1270, 1, 0, 0, 0, 296, 1274, 1, 0, 0, 0, 298, 1278, 1, 0, 0, 0, 300, 1284, 1, 0, 0, 0, 302, 1288, 1, 0, 0, 0, 304, 1292, 1, 0, 0, 0, 306, 1296, 1, 0, 0, 0, 308, 1300, 1, 0, 0, 0, 310, 1304, 1, 0, 0, 0, 312, 1308, 1, 0, 0, 0, 314, 1313, 1, 0, 0, 0, 316, 1318, 1, 0, 0, 0, 318, 1322, 1, 0, 0, 0, 320, 1326, 1, 0, 0, 0, 322, 1330, 1, 0, 0, 0, 324, 1335, 1, 0, 0, 0, 326, 1339, 1, 0, 0, 0, 328, 1344, 1, 0, 0, 0, 330, 1349, 1, 0, 0, 0, 332, 1353, 1, 0, 0, 0, 334, 1357, 1, 0, 0, 0, 336, 1361, 1, 0, 0, 0, 338, 1365, 1, 0, 0, 0, 340, 1369, 1, 0, 0, 0, 342, 1374, 1, 0, 0, 0, 344, 1379, 1, 0, 0, 0, 346, 1383, 1, 0, 0, 0, 348, 1387, 1, 0, 0, 0, 350, 1391, 1, 0, 0, 0, 352, 1396, 1, 0, 0, 0, 354, 1405, 1, 0, 0, 0, 356, 1409, 1, 0, 0, 0, 358, 1413, 1, 0, 0, 0, 360, 1417, 1, 0, 0, 0, 362, 1421, 1, 0, 0, 0, 364, 1426, 1, 0, 0, 0, 366, 1430, 1, 0, 0, 0, 368, 1434, 1, 0, 0, 0, 370, 1438, 1, 0, 0, 0, 372, 1443, 1, 0, 0, 0, 374, 1447, 1, 0, 0, 0, 376, 1451, 1, 0, 0, 0, 378, 1455, 1, 0, 0, 0, 380, 1459, 1, 0, 0, 0, 382, 1463, 1, 0, 0, 0, 384, 1469, 1, 0, 0, 0, 386, 1473, 1, 0, 0, 0, 388, 1477, 1, 0, 0, 0, 390, 1481, 1, 0, 0, 0, 392, 1485, 1, 0, 0, 0, 394, 1489, 1, 0, 0, 0, 396, 1493, 1, 0, 0, 0, 398, 1498, 1, 0, 0, 0, 400, 1502, 1, 0, 0, 0, 402, 1506, 1, 0, 0, 0, 404, 1512, 1, 0, 0, 0, 406, 1521, 1, 0, 0, 0, 408, 1525, 1, 0, 0, 0, 410, 1529, 1, 0, 0, 0, 412, 1533, 1, 0, 0, 0, 414, 1537, 1, 0, 0, 0, 416, 1541, 1, 0, 0, 0, 418, 1545, 1, 0, 0, 0, 420, 1549, 1, 0, 0, 0, 422, 1553, 1, 0, 0, 0, 424, 1558, 1, 0, 0, 0, 426, 1564, 1, 0, 0, 0, 428, 1570, 1, 0, 0, 0, 430, 1574, 1, 0, 0, 0, 432, 1578, 1, 0, 0, 0, 434, 1582, 1, 0, 0, 0, 436, 1588, 1, 0, 0, 0, 438, 1594, 1, 0, 0, 0, 440, 1598, 1, 0, 0, 0, 442, 1602, 1, 0, 0, 0, 444, 1606, 1, 0, 0, 0, 446, 1612, 1, 0, 0, 0, 448, 1618, 1, 0, 0, 0, 450, 1624, 1, 0, 0, 0, 452, 453, 7, 0, 0, 0, 453, 454, 7, 1, 0, 0, 454, 455, 7, 2, 0, 0, 455, 456, 7, 2, 0, 0, 456, 457, 7, 3, 0, 0, 457, 458, 7, 4, 0, 0, 458, 459, 7, 5, 0, 0, 459, 460, 1, 0, 0, 0, 460, 461, 6, 0, 0, 0, 461, 17, 1, 0, 0, 0, 462, 463, 7, 0, 0, 0, 463, 464, 7, 6, 0, 0, 464, 465, 7, 7, 0, 0, 465, 466, 7, 8, 0, 0, 466, 467, 1, 0, 0, 0, 467, 468, 6, 1, 1, 0, 468, 19, 1, 0, 0, 0, 469, 470, 7, 3, 0, 0, 470, 471, 7, 9, 0, 0, 471, 472, 7, 6, 0, 0, 472, 473, 7, 1, 0, 0, 473, 474, 7, 4, 0, 0, 474, 475, 7, 10, 0, 0, 475, 476, 1, 0, 0, 0, 476, 477, 6, 2, 2, 0, 477, 21, 1, 0, 0, 0, 478, 479, 7, 3, 0, 0, 479, 480, 7, 11, 0, 0, 480, 481, 7, 12, 0, 0, 481, 482, 7, 13, 0, 0, 482, 483, 1, 0, 0, 0, 483, 484, 6, 3, 0, 0, 484, 23, 1, 0, 0, 0, 485, 486, 7, 3, 0, 0, 486, 487, 7, 14, 0, 0, 487, 488, 7, 8, 0, 0, 488, 489, 7, 13, 0, 0, 489, 490, 7, 12, 0, 0, 490, 491, 7, 1, 0, 0, 491, 492, 7, 9, 0, 0, 492, 493, 1, 0, 0, 0, 493, 494, 6, 4, 3, 0, 494, 25, 1, 0, 0, 0, 495, 496, 7, 15, 0, 0, 496, 497, 7, 6, 0, 0, 497, 498, 7, 7, 0, 0, 498, 499, 7, 16, 0, 0, 499, 500, 1, 0, 0, 0, 500, 501, 6, 5, 4, 0, 501, 27, 1, 0, 0, 0, 502, 503, 7, 17, 0, 0, 503, 504, 7, 6, 0, 0, 504, 505, 7, 7, 0, 0, 505, 506, 7, 18, 0, 0, 506, 507, 1, 0, 0, 0, 507, 508, 6, 6, 0, 0, 508, 29, 1, 0, 0, 0, 509, 510, 7, 18, 0, 0, 510, 511, 7, 3, 0, 0, 511, 512, 7, 3, 0, 0, 512, 513, 7, 8, 0, 0, 513, 514, 1, 0, 0, 0, 514, 515, 6, 7, 1, 0, 515, 31, 1, 0, 0, 0, 516, 517, 7, 13, 0, 0, 517, 518, 7, 1, 0, 0, 518, 519, 7, 16, 0, 0, 519, 520, 7, 1, 0, 0, 520, 521, 7, 5, 0, 0, 521, 522, 1, 0, 0, 0, 522, 523, 6, 8, 0, 0, 523, 33, 1, 0, 0, 0, 524, 525, 7, 16, 0, 0, 525, 526, 7, 11, 0, 0, 526, 527, 5, 95, 0, 0, 527, 528, 7, 3, 0, 0, 528, 529, 7, 14, 0, 0, 529, 530, 7, 8, 0, 0, 530, 531, 7, 12, 0, 0, 531, 532, 7, 9, 0, 0, 532, 533, 7, 0, 0, 0, 533, 534, 1, 0, 0, 0, 534, 535, 6, 9, 5, 0, 535, 35, 1, 0, 0, 0, 536, 537, 7, 6, 0, 0, 537, 538, 7, 3, 0, 0, 538, 539, 7, 9, 0, 0, 539, 540, 7, 12, 0, 0, 540, 541, 7, 16, 0, 0, 541, 542, 7, 3, 0, 0, 542, 543, 1, 0, 0, 0, 543, 544, 6, 10, 6, 0, 544, 37, 1, 0, 0, 0, 545, 546, 7, 6, 0, 0, 546, 547, 7, 7, 0, 0, 547, 548, 7, 19, 0, 0, 548, 549, 1, 0, 0, 0, 549, 550, 6, 11, 0, 0, 550, 39, 1, 0, 0, 0, 551, 552, 7, 2, 0, 0, 552, 553, 7, 10, 0, 0, 553, 554, 7, 7, 0, 0, 554, 555, 7, 19, 0, 0, 555, 556, 1, 0, 0, 0, 556, 557, 6, 12, 7, 0, 557, 41, 1, 0, 0, 0, 558, 559, 7, 2, 0, 0, 559, 560, 7, 7, 0, 0, 560, 561, 7, 6, 0, 0, 561, 562, 7, 5, 0, 0, 562, 563, 1, 0, 0, 0, 563, 564, 6, 13, 0, 0, 564, 43, 1, 0, 0, 0, 565, 566, 7, 2, 0, 0, 566, 567, 7, 5, 0, 0, 567, 568, 7, 12, 0, 0, 568, 569, 7, 5, 0, 0, 569, 570, 7, 2, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 6, 14, 0, 0, 572, 45, 1, 0, 0, 0, 573, 574, 7, 19, 0, 0, 574, 575, 7, 10, 0, 0, 575, 576, 7, 3, 0, 0, 576, 577, 7, 6, 0, 0, 577, 578, 7, 3, 0, 0, 578, 579, 1, 0, 0, 0, 579, 580, 6, 15, 0, 0, 580, 47, 1, 0, 0, 0, 581, 582, 4, 16, 0, 0, 582, 583, 7, 1, 0, 0, 583, 584, 7, 9, 0, 0, 584, 585, 7, 13, 0, 0, 585, 586, 7, 1, 0, 0, 586, 587, 7, 9, 0, 0, 587, 588, 7, 3, 0, 0, 588, 589, 7, 2, 0, 0, 589, 590, 7, 5, 0, 0, 590, 591, 7, 12, 0, 0, 591, 592, 7, 5, 0, 0, 592, 593, 7, 2, 0, 0, 593, 594, 1, 0, 0, 0, 594, 595, 6, 16, 0, 0, 595, 49, 1, 0, 0, 0, 596, 597, 4, 17, 1, 0, 597, 598, 7, 13, 0, 0, 598, 599, 7, 7, 0, 0, 599, 600, 7, 7, 0, 0, 600, 601, 7, 18, 0, 0, 601, 602, 7, 20, 0, 0, 602, 603, 7, 8, 0, 0, 603, 604, 5, 95, 0, 0, 604, 605, 5, 128020, 0, 0, 605, 606, 1, 0, 0, 0, 606, 607, 6, 17, 8, 0, 607, 51, 1, 0, 0, 0, 608, 609, 4, 18, 2, 0, 609, 610, 7, 16, 0, 0, 610, 611, 7, 3, 0, 0, 611, 612, 7, 5, 0, 0, 612, 613, 7, 6, 0, 0, 613, 614, 7, 1, 0, 0, 614, 615, 7, 4, 0, 0, 615, 616, 7, 2, 0, 0, 616, 617, 1, 0, 0, 0, 617, 618, 6, 18, 9, 0, 618, 53, 1, 0, 0, 0, 619, 620, 4, 19, 3, 0, 620, 621, 7, 21, 0, 0, 621, 622, 7, 7, 0, 0, 622, 623, 7, 1, 0, 0, 623, 624, 7, 9, 0, 0, 624, 625, 1, 0, 0, 0, 625, 626, 6, 19, 10, 0, 626, 55, 1, 0, 0, 0, 627, 628, 4, 20, 4, 0, 628, 629, 7, 15, 0, 0, 629, 630, 7, 20, 0, 0, 630, 631, 7, 13, 0, 0, 631, 632, 7, 13, 0, 0, 632, 633, 1, 0, 0, 0, 633, 634, 6, 20, 10, 0, 634, 57, 1, 0, 0, 0, 635, 636, 4, 21, 5, 0, 636, 637, 7, 13, 0, 0, 637, 638, 7, 3, 0, 0, 638, 639, 7, 15, 0, 0, 639, 640, 7, 5, 0, 0, 640, 641, 1, 0, 0, 0, 641, 642, 6, 21, 10, 0, 642, 59, 1, 0, 0, 0, 643, 644, 4, 22, 6, 0, 644, 645, 7, 6, 0, 0, 645, 646, 7, 1, 0, 0, 646, 647, 7, 17, 0, 0, 647, 648, 7, 10, 0, 0, 648, 649, 7, 5, 0, 0, 649, 650, 1, 0, 0, 0, 650, 651, 6, 22, 10, 0, 651, 61, 1, 0, 0, 0, 652, 653, 4, 23, 7, 0, 653, 654, 7, 13, 0, 0, 654, 655, 7, 7, 0, 0, 655, 656, 7, 7, 0, 0, 656, 657, 7, 18, 0, 0, 657, 658, 7, 20, 0, 0, 658, 659, 7, 8, 0, 0, 659, 660, 1, 0, 0, 0, 660, 661, 6, 23, 10, 0, 661, 63, 1, 0, 0, 0, 662, 664, 8, 22, 0, 0, 663, 662, 1, 0, 0, 0, 664, 665, 1, 0, 0, 0, 665, 663, 1, 0, 0, 0, 665, 666, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 668, 6, 24, 0, 0, 668, 65, 1, 0, 0, 0, 669, 670, 5, 47, 0, 0, 670, 671, 5, 47, 0, 0, 671, 675, 1, 0, 0, 0, 672, 674, 8, 23, 0, 0, 673, 672, 1, 0, 0, 0, 674, 677, 1, 0, 0, 0, 675, 673, 1, 0, 0, 0, 675, 676, 1, 0, 0, 0, 676, 679, 1, 0, 0, 0, 677, 675, 1, 0, 0, 0, 678, 680, 5, 13, 0, 0, 679, 678, 1, 0, 0, 0, 679, 680, 1, 0, 0, 0, 680, 682, 1, 0, 0, 0, 681, 683, 5, 10, 0, 0, 682, 681, 1, 0, 0, 0, 682, 683, 1, 0, 0, 0, 683, 684, 1, 0, 0, 0, 684, 685, 6, 25, 11, 0, 685, 67, 1, 0, 0, 0, 686, 687, 5, 47, 0, 0, 687, 688, 5, 42, 0, 0, 688, 693, 1, 0, 0, 0, 689, 692, 3, 68, 26, 0, 690, 692, 9, 0, 0, 0, 691, 689, 1, 0, 0, 0, 691, 690, 1, 0, 0, 0, 692, 695, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 693, 691, 1, 0, 0, 0, 694, 696, 1, 0, 0, 0, 695, 693, 1, 0, 0, 0, 696, 697, 5, 42, 0, 0, 697, 698, 5, 47, 0, 0, 698, 699, 1, 0, 0, 0, 699, 700, 6, 26, 11, 0, 700, 69, 1, 0, 0, 0, 701, 703, 7, 24, 0, 0, 702, 701, 1, 0, 0, 0, 703, 704, 1, 0, 0, 0, 704, 702, 1, 0, 0, 0, 704, 705, 1, 0, 0, 0, 705, 706, 1, 0, 0, 0, 706, 707, 6, 27, 11, 0, 707, 71, 1, 0, 0, 0, 708, 709, 5, 124, 0, 0, 709, 710, 1, 0, 0, 0, 710, 711, 6, 28, 12, 0, 711, 73, 1, 0, 0, 0, 712, 713, 7, 25, 0, 0, 713, 75, 1, 0, 0, 0, 714, 715, 7, 26, 0, 0, 715, 77, 1, 0, 0, 0, 716, 717, 5, 92, 0, 0, 717, 718, 7, 27, 0, 0, 718, 79, 1, 0, 0, 0, 719, 720, 8, 28, 0, 0, 720, 81, 1, 0, 0, 0, 721, 723, 7, 3, 0, 0, 722, 724, 7, 29, 0, 0, 723, 722, 1, 0, 0, 0, 723, 724, 1, 0, 0, 0, 724, 726, 1, 0, 0, 0, 725, 727, 3, 74, 29, 0, 726, 725, 1, 0, 0, 0, 727, 728, 1, 0, 0, 0, 728, 726, 1, 0, 0, 0, 728, 729, 1, 0, 0, 0, 729, 83, 1, 0, 0, 0, 730, 731, 5, 64, 0, 0, 731, 85, 1, 0, 0, 0, 732, 733, 5, 96, 0, 0, 733, 87, 1, 0, 0, 0, 734, 738, 8, 30, 0, 0, 735, 736, 5, 96, 0, 0, 736, 738, 5, 96, 0, 0, 737, 734, 1, 0, 0, 0, 737, 735, 1, 0, 0, 0, 738, 89, 1, 0, 0, 0, 739, 740, 5, 95, 0, 0, 740, 91, 1, 0, 0, 0, 741, 745, 3, 76, 30, 0, 742, 745, 3, 74, 29, 0, 743, 745, 3, 90, 37, 0, 744, 741, 1, 0, 0, 0, 744, 742, 1, 0, 0, 0, 744, 743, 1, 0, 0, 0, 745, 93, 1, 0, 0, 0, 746, 751, 5, 34, 0, 0, 747, 750, 3, 78, 31, 0, 748, 750, 3, 80, 32, 0, 749, 747, 1, 0, 0, 0, 749, 748, 1, 0, 0, 0, 750, 753, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 751, 752, 1, 0, 0, 0, 752, 754, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 754, 776, 5, 34, 0, 0, 755, 756, 5, 34, 0, 0, 756, 757, 5, 34, 0, 0, 757, 758, 5, 34, 0, 0, 758, 762, 1, 0, 0, 0, 759, 761, 8, 23, 0, 0, 760, 759, 1, 0, 0, 0, 761, 764, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 762, 760, 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 762, 1, 0, 0, 0, 765, 766, 5, 34, 0, 0, 766, 767, 5, 34, 0, 0, 767, 768, 5, 34, 0, 0, 768, 770, 1, 0, 0, 0, 769, 771, 5, 34, 0, 0, 770, 769, 1, 0, 0, 0, 770, 771, 1, 0, 0, 0, 771, 773, 1, 0, 0, 0, 772, 774, 5, 34, 0, 0, 773, 772, 1, 0, 0, 0, 773, 774, 1, 0, 0, 0, 774, 776, 1, 0, 0, 0, 775, 746, 1, 0, 0, 0, 775, 755, 1, 0, 0, 0, 776, 95, 1, 0, 0, 0, 777, 779, 3, 74, 29, 0, 778, 777, 1, 0, 0, 0, 779, 780, 1, 0, 0, 0, 780, 778, 1, 0, 0, 0, 780, 781, 1, 0, 0, 0, 781, 97, 1, 0, 0, 0, 782, 784, 3, 74, 29, 0, 783, 782, 1, 0, 0, 0, 784, 785, 1, 0, 0, 0, 785, 783, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 791, 3, 116, 50, 0, 788, 790, 3, 74, 29, 0, 789, 788, 1, 0, 0, 0, 790, 793, 1, 0, 0, 0, 791, 789, 1, 0, 0, 0, 791, 792, 1, 0, 0, 0, 792, 825, 1, 0, 0, 0, 793, 791, 1, 0, 0, 0, 794, 796, 3, 116, 50, 0, 795, 797, 3, 74, 29, 0, 796, 795, 1, 0, 0, 0, 797, 798, 1, 0, 0, 0, 798, 796, 1, 0, 0, 0, 798, 799, 1, 0, 0, 0, 799, 825, 1, 0, 0, 0, 800, 802, 3, 74, 29, 0, 801, 800, 1, 0, 0, 0, 802, 803, 1, 0, 0, 0, 803, 801, 1, 0, 0, 0, 803, 804, 1, 0, 0, 0, 804, 812, 1, 0, 0, 0, 805, 809, 3, 116, 50, 0, 806, 808, 3, 74, 29, 0, 807, 806, 1, 0, 0, 0, 808, 811, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 813, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 812, 805, 1, 0, 0, 0, 812, 813, 1, 0, 0, 0, 813, 814, 1, 0, 0, 0, 814, 815, 3, 82, 33, 0, 815, 825, 1, 0, 0, 0, 816, 818, 3, 116, 50, 0, 817, 819, 3, 74, 29, 0, 818, 817, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 818, 1, 0, 0, 0, 820, 821, 1, 0, 0, 0, 821, 822, 1, 0, 0, 0, 822, 823, 3, 82, 33, 0, 823, 825, 1, 0, 0, 0, 824, 783, 1, 0, 0, 0, 824, 794, 1, 0, 0, 0, 824, 801, 1, 0, 0, 0, 824, 816, 1, 0, 0, 0, 825, 99, 1, 0, 0, 0, 826, 827, 7, 31, 0, 0, 827, 828, 7, 32, 0, 0, 828, 101, 1, 0, 0, 0, 829, 830, 7, 12, 0, 0, 830, 831, 7, 9, 0, 0, 831, 832, 7, 0, 0, 0, 832, 103, 1, 0, 0, 0, 833, 834, 7, 12, 0, 0, 834, 835, 7, 2, 0, 0, 835, 836, 7, 4, 0, 0, 836, 105, 1, 0, 0, 0, 837, 838, 5, 61, 0, 0, 838, 107, 1, 0, 0, 0, 839, 840, 5, 58, 0, 0, 840, 841, 5, 58, 0, 0, 841, 109, 1, 0, 0, 0, 842, 843, 5, 58, 0, 0, 843, 111, 1, 0, 0, 0, 844, 845, 5, 44, 0, 0, 845, 113, 1, 0, 0, 0, 846, 847, 7, 0, 0, 0, 847, 848, 7, 3, 0, 0, 848, 849, 7, 2, 0, 0, 849, 850, 7, 4, 0, 0, 850, 115, 1, 0, 0, 0, 851, 852, 5, 46, 0, 0, 852, 117, 1, 0, 0, 0, 853, 854, 7, 15, 0, 0, 854, 855, 7, 12, 0, 0, 855, 856, 7, 13, 0, 0, 856, 857, 7, 2, 0, 0, 857, 858, 7, 3, 0, 0, 858, 119, 1, 0, 0, 0, 859, 860, 7, 15, 0, 0, 860, 861, 7, 1, 0, 0, 861, 862, 7, 6, 0, 0, 862, 863, 7, 2, 0, 0, 863, 864, 7, 5, 0, 0, 864, 121, 1, 0, 0, 0, 865, 866, 7, 1, 0, 0, 866, 867, 7, 9, 0, 0, 867, 123, 1, 0, 0, 0, 868, 869, 7, 1, 0, 0, 869, 870, 7, 2, 0, 0, 870, 125, 1, 0, 0, 0, 871, 872, 7, 13, 0, 0, 872, 873, 7, 12, 0, 0, 873, 874, 7, 2, 0, 0, 874, 875, 7, 5, 0, 0, 875, 127, 1, 0, 0, 0, 876, 877, 7, 13, 0, 0, 877, 878, 7, 1, 0, 0, 878, 879, 7, 18, 0, 0, 879, 880, 7, 3, 0, 0, 880, 129, 1, 0, 0, 0, 881, 882, 5, 40, 0, 0, 882, 131, 1, 0, 0, 0, 883, 884, 7, 9, 0, 0, 884, 885, 7, 7, 0, 0, 885, 886, 7, 5, 0, 0, 886, 133, 1, 0, 0, 0, 887, 888, 7, 9, 0, 0, 888, 889, 7, 20, 0, 0, 889, 890, 7, 13, 0, 0, 890, 891, 7, 13, 0, 0, 891, 135, 1, 0, 0, 0, 892, 893, 7, 9, 0, 0, 893, 894, 7, 20, 0, 0, 894, 895, 7, 13, 0, 0, 895, 896, 7, 13, 0, 0, 896, 897, 7, 2, 0, 0, 897, 137, 1, 0, 0, 0, 898, 899, 7, 7, 0, 0, 899, 900, 7, 6, 0, 0, 900, 139, 1, 0, 0, 0, 901, 902, 5, 63, 0, 0, 902, 141, 1, 0, 0, 0, 903, 904, 7, 6, 0, 0, 904, 905, 7, 13, 0, 0, 905, 906, 7, 1, 0, 0, 906, 907, 7, 18, 0, 0, 907, 908, 7, 3, 0, 0, 908, 143, 1, 0, 0, 0, 909, 910, 5, 41, 0, 0, 910, 145, 1, 0, 0, 0, 911, 912, 7, 5, 0, 0, 912, 913, 7, 6, 0, 0, 913, 914, 7, 20, 0, 0, 914, 915, 7, 3, 0, 0, 915, 147, 1, 0, 0, 0, 916, 917, 5, 61, 0, 0, 917, 918, 5, 61, 0, 0, 918, 149, 1, 0, 0, 0, 919, 920, 5, 61, 0, 0, 920, 921, 5, 126, 0, 0, 921, 151, 1, 0, 0, 0, 922, 923, 5, 33, 0, 0, 923, 924, 5, 61, 0, 0, 924, 153, 1, 0, 0, 0, 925, 926, 5, 60, 0, 0, 926, 155, 1, 0, 0, 0, 927, 928, 5, 60, 0, 0, 928, 929, 5, 61, 0, 0, 929, 157, 1, 0, 0, 0, 930, 931, 5, 62, 0, 0, 931, 159, 1, 0, 0, 0, 932, 933, 5, 62, 0, 0, 933, 934, 5, 61, 0, 0, 934, 161, 1, 0, 0, 0, 935, 936, 5, 43, 0, 0, 936, 163, 1, 0, 0, 0, 937, 938, 5, 45, 0, 0, 938, 165, 1, 0, 0, 0, 939, 940, 5, 42, 0, 0, 940, 167, 1, 0, 0, 0, 941, 942, 5, 47, 0, 0, 942, 169, 1, 0, 0, 0, 943, 944, 5, 37, 0, 0, 944, 171, 1, 0, 0, 0, 945, 946, 4, 78, 8, 0, 946, 947, 5, 123, 0, 0, 947, 173, 1, 0, 0, 0, 948, 949, 4, 79, 9, 0, 949, 950, 5, 125, 0, 0, 950, 175, 1, 0, 0, 0, 951, 952, 3, 46, 15, 0, 952, 953, 1, 0, 0, 0, 953, 954, 6, 80, 13, 0, 954, 177, 1, 0, 0, 0, 955, 958, 3, 140, 62, 0, 956, 959, 3, 76, 30, 0, 957, 959, 3, 90, 37, 0, 958, 956, 1, 0, 0, 0, 958, 957, 1, 0, 0, 0, 959, 963, 1, 0, 0, 0, 960, 962, 3, 92, 38, 0, 961, 960, 1, 0, 0, 0, 962, 965, 1, 0, 0, 0, 963, 961, 1, 0, 0, 0, 963, 964, 1, 0, 0, 0, 964, 973, 1, 0, 0, 0, 965, 963, 1, 0, 0, 0, 966, 968, 3, 140, 62, 0, 967, 969, 3, 74, 29, 0, 968, 967, 1, 0, 0, 0, 969, 970, 1, 0, 0, 0, 970, 968, 1, 0, 0, 0, 970, 971, 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 955, 1, 0, 0, 0, 972, 966, 1, 0, 0, 0, 973, 179, 1, 0, 0, 0, 974, 975, 5, 91, 0, 0, 975, 976, 1, 0, 0, 0, 976, 977, 6, 82, 0, 0, 977, 978, 6, 82, 0, 0, 978, 181, 1, 0, 0, 0, 979, 980, 5, 93, 0, 0, 980, 981, 1, 0, 0, 0, 981, 982, 6, 83, 12, 0, 982, 983, 6, 83, 12, 0, 983, 183, 1, 0, 0, 0, 984, 988, 3, 76, 30, 0, 985, 987, 3, 92, 38, 0, 986, 985, 1, 0, 0, 0, 987, 990, 1, 0, 0, 0, 988, 986, 1, 0, 0, 0, 988, 989, 1, 0, 0, 0, 989, 1001, 1, 0, 0, 0, 990, 988, 1, 0, 0, 0, 991, 994, 3, 90, 37, 0, 992, 994, 3, 84, 34, 0, 993, 991, 1, 0, 0, 0, 993, 992, 1, 0, 0, 0, 994, 996, 1, 0, 0, 0, 995, 997, 3, 92, 38, 0, 996, 995, 1, 0, 0, 0, 997, 998, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 998, 999, 1, 0, 0, 0, 999, 1001, 1, 0, 0, 0, 1000, 984, 1, 0, 0, 0, 1000, 993, 1, 0, 0, 0, 1001, 185, 1, 0, 0, 0, 1002, 1004, 3, 86, 35, 0, 1003, 1005, 3, 88, 36, 0, 1004, 1003, 1, 0, 0, 0, 1005, 1006, 1, 0, 0, 0, 1006, 1004, 1, 0, 0, 0, 1006, 1007, 1, 0, 0, 0, 1007, 1008, 1, 0, 0, 0, 1008, 1009, 3, 86, 35, 0, 1009, 187, 1, 0, 0, 0, 1010, 1011, 3, 186, 85, 0, 1011, 189, 1, 0, 0, 0, 1012, 1013, 3, 66, 25, 0, 1013, 1014, 1, 0, 0, 0, 1014, 1015, 6, 87, 11, 0, 1015, 191, 1, 0, 0, 0, 1016, 1017, 3, 68, 26, 0, 1017, 1018, 1, 0, 0, 0, 1018, 1019, 6, 88, 11, 0, 1019, 193, 1, 0, 0, 0, 1020, 1021, 3, 70, 27, 0, 1021, 1022, 1, 0, 0, 0, 1022, 1023, 6, 89, 11, 0, 1023, 195, 1, 0, 0, 0, 1024, 1025, 3, 180, 82, 0, 1025, 1026, 1, 0, 0, 0, 1026, 1027, 6, 90, 14, 0, 1027, 1028, 6, 90, 15, 0, 1028, 197, 1, 0, 0, 0, 1029, 1030, 3, 72, 28, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1032, 6, 91, 16, 0, 1032, 1033, 6, 91, 12, 0, 1033, 199, 1, 0, 0, 0, 1034, 1035, 3, 70, 27, 0, 1035, 1036, 1, 0, 0, 0, 1036, 1037, 6, 92, 11, 0, 1037, 201, 1, 0, 0, 0, 1038, 1039, 3, 66, 25, 0, 1039, 1040, 1, 0, 0, 0, 1040, 1041, 6, 93, 11, 0, 1041, 203, 1, 0, 0, 0, 1042, 1043, 3, 68, 26, 0, 1043, 1044, 1, 0, 0, 0, 1044, 1045, 6, 94, 11, 0, 1045, 205, 1, 0, 0, 0, 1046, 1047, 3, 72, 28, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1049, 6, 95, 16, 0, 1049, 1050, 6, 95, 12, 0, 1050, 207, 1, 0, 0, 0, 1051, 1052, 3, 180, 82, 0, 1052, 1053, 1, 0, 0, 0, 1053, 1054, 6, 96, 14, 0, 1054, 209, 1, 0, 0, 0, 1055, 1056, 3, 182, 83, 0, 1056, 1057, 1, 0, 0, 0, 1057, 1058, 6, 97, 17, 0, 1058, 211, 1, 0, 0, 0, 1059, 1060, 3, 110, 47, 0, 1060, 1061, 1, 0, 0, 0, 1061, 1062, 6, 98, 18, 0, 1062, 213, 1, 0, 0, 0, 1063, 1064, 3, 112, 48, 0, 1064, 1065, 1, 0, 0, 0, 1065, 1066, 6, 99, 19, 0, 1066, 215, 1, 0, 0, 0, 1067, 1068, 3, 106, 45, 0, 1068, 1069, 1, 0, 0, 0, 1069, 1070, 6, 100, 20, 0, 1070, 217, 1, 0, 0, 0, 1071, 1072, 7, 16, 0, 0, 1072, 1073, 7, 3, 0, 0, 1073, 1074, 7, 5, 0, 0, 1074, 1075, 7, 12, 0, 0, 1075, 1076, 7, 0, 0, 0, 1076, 1077, 7, 12, 0, 0, 1077, 1078, 7, 5, 0, 0, 1078, 1079, 7, 12, 0, 0, 1079, 219, 1, 0, 0, 0, 1080, 1084, 8, 33, 0, 0, 1081, 1082, 5, 47, 0, 0, 1082, 1084, 8, 34, 0, 0, 1083, 1080, 1, 0, 0, 0, 1083, 1081, 1, 0, 0, 0, 1084, 221, 1, 0, 0, 0, 1085, 1087, 3, 220, 102, 0, 1086, 1085, 1, 0, 0, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1086, 1, 0, 0, 0, 1088, 1089, 1, 0, 0, 0, 1089, 223, 1, 0, 0, 0, 1090, 1091, 3, 222, 103, 0, 1091, 1092, 1, 0, 0, 0, 1092, 1093, 6, 104, 21, 0, 1093, 225, 1, 0, 0, 0, 1094, 1095, 3, 94, 39, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, 6, 105, 22, 0, 1097, 227, 1, 0, 0, 0, 1098, 1099, 3, 66, 25, 0, 1099, 1100, 1, 0, 0, 0, 1100, 1101, 6, 106, 11, 0, 1101, 229, 1, 0, 0, 0, 1102, 1103, 3, 68, 26, 0, 1103, 1104, 1, 0, 0, 0, 1104, 1105, 6, 107, 11, 0, 1105, 231, 1, 0, 0, 0, 1106, 1107, 3, 70, 27, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 108, 11, 0, 1109, 233, 1, 0, 0, 0, 1110, 1111, 3, 72, 28, 0, 1111, 1112, 1, 0, 0, 0, 1112, 1113, 6, 109, 16, 0, 1113, 1114, 6, 109, 12, 0, 1114, 235, 1, 0, 0, 0, 1115, 1116, 3, 116, 50, 0, 1116, 1117, 1, 0, 0, 0, 1117, 1118, 6, 110, 23, 0, 1118, 237, 1, 0, 0, 0, 1119, 1120, 3, 112, 48, 0, 1120, 1121, 1, 0, 0, 0, 1121, 1122, 6, 111, 19, 0, 1122, 239, 1, 0, 0, 0, 1123, 1124, 4, 112, 10, 0, 1124, 1125, 3, 140, 62, 0, 1125, 1126, 1, 0, 0, 0, 1126, 1127, 6, 112, 24, 0, 1127, 241, 1, 0, 0, 0, 1128, 1129, 4, 113, 11, 0, 1129, 1130, 3, 178, 81, 0, 1130, 1131, 1, 0, 0, 0, 1131, 1132, 6, 113, 25, 0, 1132, 243, 1, 0, 0, 0, 1133, 1138, 3, 76, 30, 0, 1134, 1138, 3, 74, 29, 0, 1135, 1138, 3, 90, 37, 0, 1136, 1138, 3, 166, 75, 0, 1137, 1133, 1, 0, 0, 0, 1137, 1134, 1, 0, 0, 0, 1137, 1135, 1, 0, 0, 0, 1137, 1136, 1, 0, 0, 0, 1138, 245, 1, 0, 0, 0, 1139, 1142, 3, 76, 30, 0, 1140, 1142, 3, 166, 75, 0, 1141, 1139, 1, 0, 0, 0, 1141, 1140, 1, 0, 0, 0, 1142, 1146, 1, 0, 0, 0, 1143, 1145, 3, 244, 114, 0, 1144, 1143, 1, 0, 0, 0, 1145, 1148, 1, 0, 0, 0, 1146, 1144, 1, 0, 0, 0, 1146, 1147, 1, 0, 0, 0, 1147, 1159, 1, 0, 0, 0, 1148, 1146, 1, 0, 0, 0, 1149, 1152, 3, 90, 37, 0, 1150, 1152, 3, 84, 34, 0, 1151, 1149, 1, 0, 0, 0, 1151, 1150, 1, 0, 0, 0, 1152, 1154, 1, 0, 0, 0, 1153, 1155, 3, 244, 114, 0, 1154, 1153, 1, 0, 0, 0, 1155, 1156, 1, 0, 0, 0, 1156, 1154, 1, 0, 0, 0, 1156, 1157, 1, 0, 0, 0, 1157, 1159, 1, 0, 0, 0, 1158, 1141, 1, 0, 0, 0, 1158, 1151, 1, 0, 0, 0, 1159, 247, 1, 0, 0, 0, 1160, 1163, 3, 246, 115, 0, 1161, 1163, 3, 186, 85, 0, 1162, 1160, 1, 0, 0, 0, 1162, 1161, 1, 0, 0, 0, 1163, 1164, 1, 0, 0, 0, 1164, 1162, 1, 0, 0, 0, 1164, 1165, 1, 0, 0, 0, 1165, 249, 1, 0, 0, 0, 1166, 1167, 3, 66, 25, 0, 1167, 1168, 1, 0, 0, 0, 1168, 1169, 6, 117, 11, 0, 1169, 251, 1, 0, 0, 0, 1170, 1171, 3, 68, 26, 0, 1171, 1172, 1, 0, 0, 0, 1172, 1173, 6, 118, 11, 0, 1173, 253, 1, 0, 0, 0, 1174, 1175, 3, 70, 27, 0, 1175, 1176, 1, 0, 0, 0, 1176, 1177, 6, 119, 11, 0, 1177, 255, 1, 0, 0, 0, 1178, 1179, 3, 72, 28, 0, 1179, 1180, 1, 0, 0, 0, 1180, 1181, 6, 120, 16, 0, 1181, 1182, 6, 120, 12, 0, 1182, 257, 1, 0, 0, 0, 1183, 1184, 3, 106, 45, 0, 1184, 1185, 1, 0, 0, 0, 1185, 1186, 6, 121, 20, 0, 1186, 259, 1, 0, 0, 0, 1187, 1188, 3, 112, 48, 0, 1188, 1189, 1, 0, 0, 0, 1189, 1190, 6, 122, 19, 0, 1190, 261, 1, 0, 0, 0, 1191, 1192, 3, 116, 50, 0, 1192, 1193, 1, 0, 0, 0, 1193, 1194, 6, 123, 23, 0, 1194, 263, 1, 0, 0, 0, 1195, 1196, 4, 124, 12, 0, 1196, 1197, 3, 140, 62, 0, 1197, 1198, 1, 0, 0, 0, 1198, 1199, 6, 124, 24, 0, 1199, 265, 1, 0, 0, 0, 1200, 1201, 4, 125, 13, 0, 1201, 1202, 3, 178, 81, 0, 1202, 1203, 1, 0, 0, 0, 1203, 1204, 6, 125, 25, 0, 1204, 267, 1, 0, 0, 0, 1205, 1206, 7, 12, 0, 0, 1206, 1207, 7, 2, 0, 0, 1207, 269, 1, 0, 0, 0, 1208, 1209, 3, 248, 116, 0, 1209, 1210, 1, 0, 0, 0, 1210, 1211, 6, 127, 26, 0, 1211, 271, 1, 0, 0, 0, 1212, 1213, 3, 66, 25, 0, 1213, 1214, 1, 0, 0, 0, 1214, 1215, 6, 128, 11, 0, 1215, 273, 1, 0, 0, 0, 1216, 1217, 3, 68, 26, 0, 1217, 1218, 1, 0, 0, 0, 1218, 1219, 6, 129, 11, 0, 1219, 275, 1, 0, 0, 0, 1220, 1221, 3, 70, 27, 0, 1221, 1222, 1, 0, 0, 0, 1222, 1223, 6, 130, 11, 0, 1223, 277, 1, 0, 0, 0, 1224, 1225, 3, 72, 28, 0, 1225, 1226, 1, 0, 0, 0, 1226, 1227, 6, 131, 16, 0, 1227, 1228, 6, 131, 12, 0, 1228, 279, 1, 0, 0, 0, 1229, 1230, 3, 180, 82, 0, 1230, 1231, 1, 0, 0, 0, 1231, 1232, 6, 132, 14, 0, 1232, 1233, 6, 132, 27, 0, 1233, 281, 1, 0, 0, 0, 1234, 1235, 7, 7, 0, 0, 1235, 1236, 7, 9, 0, 0, 1236, 1237, 1, 0, 0, 0, 1237, 1238, 6, 133, 28, 0, 1238, 283, 1, 0, 0, 0, 1239, 1240, 7, 19, 0, 0, 1240, 1241, 7, 1, 0, 0, 1241, 1242, 7, 5, 0, 0, 1242, 1243, 7, 10, 0, 0, 1243, 1244, 1, 0, 0, 0, 1244, 1245, 6, 134, 28, 0, 1245, 285, 1, 0, 0, 0, 1246, 1247, 8, 35, 0, 0, 1247, 287, 1, 0, 0, 0, 1248, 1250, 3, 286, 135, 0, 1249, 1248, 1, 0, 0, 0, 1250, 1251, 1, 0, 0, 0, 1251, 1249, 1, 0, 0, 0, 1251, 1252, 1, 0, 0, 0, 1252, 1253, 1, 0, 0, 0, 1253, 1254, 3, 110, 47, 0, 1254, 1256, 1, 0, 0, 0, 1255, 1249, 1, 0, 0, 0, 1255, 1256, 1, 0, 0, 0, 1256, 1258, 1, 0, 0, 0, 1257, 1259, 3, 286, 135, 0, 1258, 1257, 1, 0, 0, 0, 1259, 1260, 1, 0, 0, 0, 1260, 1258, 1, 0, 0, 0, 1260, 1261, 1, 0, 0, 0, 1261, 289, 1, 0, 0, 0, 1262, 1263, 3, 288, 136, 0, 1263, 1264, 1, 0, 0, 0, 1264, 1265, 6, 137, 29, 0, 1265, 291, 1, 0, 0, 0, 1266, 1267, 3, 66, 25, 0, 1267, 1268, 1, 0, 0, 0, 1268, 1269, 6, 138, 11, 0, 1269, 293, 1, 0, 0, 0, 1270, 1271, 3, 68, 26, 0, 1271, 1272, 1, 0, 0, 0, 1272, 1273, 6, 139, 11, 0, 1273, 295, 1, 0, 0, 0, 1274, 1275, 3, 70, 27, 0, 1275, 1276, 1, 0, 0, 0, 1276, 1277, 6, 140, 11, 0, 1277, 297, 1, 0, 0, 0, 1278, 1279, 3, 72, 28, 0, 1279, 1280, 1, 0, 0, 0, 1280, 1281, 6, 141, 16, 0, 1281, 1282, 6, 141, 12, 0, 1282, 1283, 6, 141, 12, 0, 1283, 299, 1, 0, 0, 0, 1284, 1285, 3, 106, 45, 0, 1285, 1286, 1, 0, 0, 0, 1286, 1287, 6, 142, 20, 0, 1287, 301, 1, 0, 0, 0, 1288, 1289, 3, 112, 48, 0, 1289, 1290, 1, 0, 0, 0, 1290, 1291, 6, 143, 19, 0, 1291, 303, 1, 0, 0, 0, 1292, 1293, 3, 116, 50, 0, 1293, 1294, 1, 0, 0, 0, 1294, 1295, 6, 144, 23, 0, 1295, 305, 1, 0, 0, 0, 1296, 1297, 3, 284, 134, 0, 1297, 1298, 1, 0, 0, 0, 1298, 1299, 6, 145, 30, 0, 1299, 307, 1, 0, 0, 0, 1300, 1301, 3, 248, 116, 0, 1301, 1302, 1, 0, 0, 0, 1302, 1303, 6, 146, 26, 0, 1303, 309, 1, 0, 0, 0, 1304, 1305, 3, 188, 86, 0, 1305, 1306, 1, 0, 0, 0, 1306, 1307, 6, 147, 31, 0, 1307, 311, 1, 0, 0, 0, 1308, 1309, 4, 148, 14, 0, 1309, 1310, 3, 140, 62, 0, 1310, 1311, 1, 0, 0, 0, 1311, 1312, 6, 148, 24, 0, 1312, 313, 1, 0, 0, 0, 1313, 1314, 4, 149, 15, 0, 1314, 1315, 3, 178, 81, 0, 1315, 1316, 1, 0, 0, 0, 1316, 1317, 6, 149, 25, 0, 1317, 315, 1, 0, 0, 0, 1318, 1319, 3, 66, 25, 0, 1319, 1320, 1, 0, 0, 0, 1320, 1321, 6, 150, 11, 0, 1321, 317, 1, 0, 0, 0, 1322, 1323, 3, 68, 26, 0, 1323, 1324, 1, 0, 0, 0, 1324, 1325, 6, 151, 11, 0, 1325, 319, 1, 0, 0, 0, 1326, 1327, 3, 70, 27, 0, 1327, 1328, 1, 0, 0, 0, 1328, 1329, 6, 152, 11, 0, 1329, 321, 1, 0, 0, 0, 1330, 1331, 3, 72, 28, 0, 1331, 1332, 1, 0, 0, 0, 1332, 1333, 6, 153, 16, 0, 1333, 1334, 6, 153, 12, 0, 1334, 323, 1, 0, 0, 0, 1335, 1336, 3, 116, 50, 0, 1336, 1337, 1, 0, 0, 0, 1337, 1338, 6, 154, 23, 0, 1338, 325, 1, 0, 0, 0, 1339, 1340, 4, 155, 16, 0, 1340, 1341, 3, 140, 62, 0, 1341, 1342, 1, 0, 0, 0, 1342, 1343, 6, 155, 24, 0, 1343, 327, 1, 0, 0, 0, 1344, 1345, 4, 156, 17, 0, 1345, 1346, 3, 178, 81, 0, 1346, 1347, 1, 0, 0, 0, 1347, 1348, 6, 156, 25, 0, 1348, 329, 1, 0, 0, 0, 1349, 1350, 3, 188, 86, 0, 1350, 1351, 1, 0, 0, 0, 1351, 1352, 6, 157, 31, 0, 1352, 331, 1, 0, 0, 0, 1353, 1354, 3, 184, 84, 0, 1354, 1355, 1, 0, 0, 0, 1355, 1356, 6, 158, 32, 0, 1356, 333, 1, 0, 0, 0, 1357, 1358, 3, 66, 25, 0, 1358, 1359, 1, 0, 0, 0, 1359, 1360, 6, 159, 11, 0, 1360, 335, 1, 0, 0, 0, 1361, 1362, 3, 68, 26, 0, 1362, 1363, 1, 0, 0, 0, 1363, 1364, 6, 160, 11, 0, 1364, 337, 1, 0, 0, 0, 1365, 1366, 3, 70, 27, 0, 1366, 1367, 1, 0, 0, 0, 1367, 1368, 6, 161, 11, 0, 1368, 339, 1, 0, 0, 0, 1369, 1370, 3, 72, 28, 0, 1370, 1371, 1, 0, 0, 0, 1371, 1372, 6, 162, 16, 0, 1372, 1373, 6, 162, 12, 0, 1373, 341, 1, 0, 0, 0, 1374, 1375, 7, 1, 0, 0, 1375, 1376, 7, 9, 0, 0, 1376, 1377, 7, 15, 0, 0, 1377, 1378, 7, 7, 0, 0, 1378, 343, 1, 0, 0, 0, 1379, 1380, 3, 66, 25, 0, 1380, 1381, 1, 0, 0, 0, 1381, 1382, 6, 164, 11, 0, 1382, 345, 1, 0, 0, 0, 1383, 1384, 3, 68, 26, 0, 1384, 1385, 1, 0, 0, 0, 1385, 1386, 6, 165, 11, 0, 1386, 347, 1, 0, 0, 0, 1387, 1388, 3, 70, 27, 0, 1388, 1389, 1, 0, 0, 0, 1389, 1390, 6, 166, 11, 0, 1390, 349, 1, 0, 0, 0, 1391, 1392, 3, 182, 83, 0, 1392, 1393, 1, 0, 0, 0, 1393, 1394, 6, 167, 17, 0, 1394, 1395, 6, 167, 12, 0, 1395, 351, 1, 0, 0, 0, 1396, 1397, 3, 110, 47, 0, 1397, 1398, 1, 0, 0, 0, 1398, 1399, 6, 168, 18, 0, 1399, 353, 1, 0, 0, 0, 1400, 1406, 3, 84, 34, 0, 1401, 1406, 3, 74, 29, 0, 1402, 1406, 3, 116, 50, 0, 1403, 1406, 3, 76, 30, 0, 1404, 1406, 3, 90, 37, 0, 1405, 1400, 1, 0, 0, 0, 1405, 1401, 1, 0, 0, 0, 1405, 1402, 1, 0, 0, 0, 1405, 1403, 1, 0, 0, 0, 1405, 1404, 1, 0, 0, 0, 1406, 1407, 1, 0, 0, 0, 1407, 1405, 1, 0, 0, 0, 1407, 1408, 1, 0, 0, 0, 1408, 355, 1, 0, 0, 0, 1409, 1410, 3, 66, 25, 0, 1410, 1411, 1, 0, 0, 0, 1411, 1412, 6, 170, 11, 0, 1412, 357, 1, 0, 0, 0, 1413, 1414, 3, 68, 26, 0, 1414, 1415, 1, 0, 0, 0, 1415, 1416, 6, 171, 11, 0, 1416, 359, 1, 0, 0, 0, 1417, 1418, 3, 70, 27, 0, 1418, 1419, 1, 0, 0, 0, 1419, 1420, 6, 172, 11, 0, 1420, 361, 1, 0, 0, 0, 1421, 1422, 3, 72, 28, 0, 1422, 1423, 1, 0, 0, 0, 1423, 1424, 6, 173, 16, 0, 1424, 1425, 6, 173, 12, 0, 1425, 363, 1, 0, 0, 0, 1426, 1427, 3, 110, 47, 0, 1427, 1428, 1, 0, 0, 0, 1428, 1429, 6, 174, 18, 0, 1429, 365, 1, 0, 0, 0, 1430, 1431, 3, 112, 48, 0, 1431, 1432, 1, 0, 0, 0, 1432, 1433, 6, 175, 19, 0, 1433, 367, 1, 0, 0, 0, 1434, 1435, 3, 116, 50, 0, 1435, 1436, 1, 0, 0, 0, 1436, 1437, 6, 176, 23, 0, 1437, 369, 1, 0, 0, 0, 1438, 1439, 3, 282, 133, 0, 1439, 1440, 1, 0, 0, 0, 1440, 1441, 6, 177, 33, 0, 1441, 1442, 6, 177, 34, 0, 1442, 371, 1, 0, 0, 0, 1443, 1444, 3, 222, 103, 0, 1444, 1445, 1, 0, 0, 0, 1445, 1446, 6, 178, 21, 0, 1446, 373, 1, 0, 0, 0, 1447, 1448, 3, 94, 39, 0, 1448, 1449, 1, 0, 0, 0, 1449, 1450, 6, 179, 22, 0, 1450, 375, 1, 0, 0, 0, 1451, 1452, 3, 66, 25, 0, 1452, 1453, 1, 0, 0, 0, 1453, 1454, 6, 180, 11, 0, 1454, 377, 1, 0, 0, 0, 1455, 1456, 3, 68, 26, 0, 1456, 1457, 1, 0, 0, 0, 1457, 1458, 6, 181, 11, 0, 1458, 379, 1, 0, 0, 0, 1459, 1460, 3, 70, 27, 0, 1460, 1461, 1, 0, 0, 0, 1461, 1462, 6, 182, 11, 0, 1462, 381, 1, 0, 0, 0, 1463, 1464, 3, 72, 28, 0, 1464, 1465, 1, 0, 0, 0, 1465, 1466, 6, 183, 16, 0, 1466, 1467, 6, 183, 12, 0, 1467, 1468, 6, 183, 12, 0, 1468, 383, 1, 0, 0, 0, 1469, 1470, 3, 112, 48, 0, 1470, 1471, 1, 0, 0, 0, 1471, 1472, 6, 184, 19, 0, 1472, 385, 1, 0, 0, 0, 1473, 1474, 3, 116, 50, 0, 1474, 1475, 1, 0, 0, 0, 1475, 1476, 6, 185, 23, 0, 1476, 387, 1, 0, 0, 0, 1477, 1478, 3, 248, 116, 0, 1478, 1479, 1, 0, 0, 0, 1479, 1480, 6, 186, 26, 0, 1480, 389, 1, 0, 0, 0, 1481, 1482, 3, 66, 25, 0, 1482, 1483, 1, 0, 0, 0, 1483, 1484, 6, 187, 11, 0, 1484, 391, 1, 0, 0, 0, 1485, 1486, 3, 68, 26, 0, 1486, 1487, 1, 0, 0, 0, 1487, 1488, 6, 188, 11, 0, 1488, 393, 1, 0, 0, 0, 1489, 1490, 3, 70, 27, 0, 1490, 1491, 1, 0, 0, 0, 1491, 1492, 6, 189, 11, 0, 1492, 395, 1, 0, 0, 0, 1493, 1494, 3, 72, 28, 0, 1494, 1495, 1, 0, 0, 0, 1495, 1496, 6, 190, 16, 0, 1496, 1497, 6, 190, 12, 0, 1497, 397, 1, 0, 0, 0, 1498, 1499, 3, 54, 19, 0, 1499, 1500, 1, 0, 0, 0, 1500, 1501, 6, 191, 35, 0, 1501, 399, 1, 0, 0, 0, 1502, 1503, 3, 268, 126, 0, 1503, 1504, 1, 0, 0, 0, 1504, 1505, 6, 192, 36, 0, 1505, 401, 1, 0, 0, 0, 1506, 1507, 3, 282, 133, 0, 1507, 1508, 1, 0, 0, 0, 1508, 1509, 6, 193, 33, 0, 1509, 1510, 6, 193, 12, 0, 1510, 1511, 6, 193, 0, 0, 1511, 403, 1, 0, 0, 0, 1512, 1513, 7, 20, 0, 0, 1513, 1514, 7, 2, 0, 0, 1514, 1515, 7, 1, 0, 0, 1515, 1516, 7, 9, 0, 0, 1516, 1517, 7, 17, 0, 0, 1517, 1518, 1, 0, 0, 0, 1518, 1519, 6, 194, 12, 0, 1519, 1520, 6, 194, 0, 0, 1520, 405, 1, 0, 0, 0, 1521, 1522, 3, 222, 103, 0, 1522, 1523, 1, 0, 0, 0, 1523, 1524, 6, 195, 21, 0, 1524, 407, 1, 0, 0, 0, 1525, 1526, 3, 94, 39, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 6, 196, 22, 0, 1528, 409, 1, 0, 0, 0, 1529, 1530, 3, 110, 47, 0, 1530, 1531, 1, 0, 0, 0, 1531, 1532, 6, 197, 18, 0, 1532, 411, 1, 0, 0, 0, 1533, 1534, 3, 184, 84, 0, 1534, 1535, 1, 0, 0, 0, 1535, 1536, 6, 198, 32, 0, 1536, 413, 1, 0, 0, 0, 1537, 1538, 3, 188, 86, 0, 1538, 1539, 1, 0, 0, 0, 1539, 1540, 6, 199, 31, 0, 1540, 415, 1, 0, 0, 0, 1541, 1542, 3, 66, 25, 0, 1542, 1543, 1, 0, 0, 0, 1543, 1544, 6, 200, 11, 0, 1544, 417, 1, 0, 0, 0, 1545, 1546, 3, 68, 26, 0, 1546, 1547, 1, 0, 0, 0, 1547, 1548, 6, 201, 11, 0, 1548, 419, 1, 0, 0, 0, 1549, 1550, 3, 70, 27, 0, 1550, 1551, 1, 0, 0, 0, 1551, 1552, 6, 202, 11, 0, 1552, 421, 1, 0, 0, 0, 1553, 1554, 3, 72, 28, 0, 1554, 1555, 1, 0, 0, 0, 1555, 1556, 6, 203, 16, 0, 1556, 1557, 6, 203, 12, 0, 1557, 423, 1, 0, 0, 0, 1558, 1559, 3, 222, 103, 0, 1559, 1560, 1, 0, 0, 0, 1560, 1561, 6, 204, 21, 0, 1561, 1562, 6, 204, 12, 0, 1562, 1563, 6, 204, 37, 0, 1563, 425, 1, 0, 0, 0, 1564, 1565, 3, 94, 39, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, 6, 205, 22, 0, 1567, 1568, 6, 205, 12, 0, 1568, 1569, 6, 205, 37, 0, 1569, 427, 1, 0, 0, 0, 1570, 1571, 3, 66, 25, 0, 1571, 1572, 1, 0, 0, 0, 1572, 1573, 6, 206, 11, 0, 1573, 429, 1, 0, 0, 0, 1574, 1575, 3, 68, 26, 0, 1575, 1576, 1, 0, 0, 0, 1576, 1577, 6, 207, 11, 0, 1577, 431, 1, 0, 0, 0, 1578, 1579, 3, 70, 27, 0, 1579, 1580, 1, 0, 0, 0, 1580, 1581, 6, 208, 11, 0, 1581, 433, 1, 0, 0, 0, 1582, 1583, 3, 110, 47, 0, 1583, 1584, 1, 0, 0, 0, 1584, 1585, 6, 209, 18, 0, 1585, 1586, 6, 209, 12, 0, 1586, 1587, 6, 209, 9, 0, 1587, 435, 1, 0, 0, 0, 1588, 1589, 3, 112, 48, 0, 1589, 1590, 1, 0, 0, 0, 1590, 1591, 6, 210, 19, 0, 1591, 1592, 6, 210, 12, 0, 1592, 1593, 6, 210, 9, 0, 1593, 437, 1, 0, 0, 0, 1594, 1595, 3, 66, 25, 0, 1595, 1596, 1, 0, 0, 0, 1596, 1597, 6, 211, 11, 0, 1597, 439, 1, 0, 0, 0, 1598, 1599, 3, 68, 26, 0, 1599, 1600, 1, 0, 0, 0, 1600, 1601, 6, 212, 11, 0, 1601, 441, 1, 0, 0, 0, 1602, 1603, 3, 70, 27, 0, 1603, 1604, 1, 0, 0, 0, 1604, 1605, 6, 213, 11, 0, 1605, 443, 1, 0, 0, 0, 1606, 1607, 3, 188, 86, 0, 1607, 1608, 1, 0, 0, 0, 1608, 1609, 6, 214, 12, 0, 1609, 1610, 6, 214, 0, 0, 1610, 1611, 6, 214, 31, 0, 1611, 445, 1, 0, 0, 0, 1612, 1613, 3, 184, 84, 0, 1613, 1614, 1, 0, 0, 0, 1614, 1615, 6, 215, 12, 0, 1615, 1616, 6, 215, 0, 0, 1616, 1617, 6, 215, 32, 0, 1617, 447, 1, 0, 0, 0, 1618, 1619, 3, 100, 42, 0, 1619, 1620, 1, 0, 0, 0, 1620, 1621, 6, 216, 12, 0, 1621, 1622, 6, 216, 0, 0, 1622, 1623, 6, 216, 38, 0, 1623, 449, 1, 0, 0, 0, 1624, 1625, 3, 72, 28, 0, 1625, 1626, 1, 0, 0, 0, 1626, 1627, 6, 217, 16, 0, 1627, 1628, 6, 217, 12, 0, 1628, 451, 1, 0, 0, 0, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 665, 675, 679, 682, 691, 693, 704, 723, 728, 737, 744, 749, 751, 762, 770, 773, 775, 780, 785, 791, 798, 803, 809, 812, 820, 824, 958, 963, 970, 972, 988, 993, 998, 1000, 1006, 1083, 1088, 1137, 1141, 1146, 1151, 1156, 1158, 1162, 1164, 1251, 1255, 1260, 1405, 1407, 39, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 11, 0, 5, 14, 0, 5, 13, 0, 0, 1, 0, 4, 0, 0, 7, 16, 0, 7, 72, 0, 5, 0, 0, 7, 29, 0, 7, 73, 0, 7, 38, 0, 7, 39, 0, 7, 36, 0, 7, 83, 0, 7, 30, 0, 7, 41, 0, 7, 53, 0, 7, 71, 0, 7, 87, 0, 5, 10, 0, 5, 7, 0, 7, 97, 0, 7, 96, 0, 7, 75, 0, 7, 74, 0, 7, 95, 0, 5, 12, 0, 7, 20, 0, 7, 91, 0, 5, 15, 0, 7, 33, 0] \ No newline at end of file +[4, 0, 130, 1617, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, 2, 202, 7, 202, 2, 203, 7, 203, 2, 204, 7, 204, 2, 205, 7, 205, 2, 206, 7, 206, 2, 207, 7, 207, 2, 208, 7, 208, 2, 209, 7, 209, 2, 210, 7, 210, 2, 211, 7, 211, 2, 212, 7, 212, 2, 213, 7, 213, 2, 214, 7, 214, 2, 215, 7, 215, 2, 216, 7, 216, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 4, 23, 653, 8, 23, 11, 23, 12, 23, 654, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 663, 8, 24, 10, 24, 12, 24, 666, 9, 24, 1, 24, 3, 24, 669, 8, 24, 1, 24, 3, 24, 672, 8, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 5, 25, 681, 8, 25, 10, 25, 12, 25, 684, 9, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 4, 26, 692, 8, 26, 11, 26, 12, 26, 693, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 32, 1, 32, 3, 32, 713, 8, 32, 1, 32, 4, 32, 716, 8, 32, 11, 32, 12, 32, 717, 1, 33, 1, 33, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 3, 35, 727, 8, 35, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 3, 37, 734, 8, 37, 1, 38, 1, 38, 1, 38, 5, 38, 739, 8, 38, 10, 38, 12, 38, 742, 9, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 750, 8, 38, 10, 38, 12, 38, 753, 9, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 760, 8, 38, 1, 38, 3, 38, 763, 8, 38, 3, 38, 765, 8, 38, 1, 39, 4, 39, 768, 8, 39, 11, 39, 12, 39, 769, 1, 40, 4, 40, 773, 8, 40, 11, 40, 12, 40, 774, 1, 40, 1, 40, 5, 40, 779, 8, 40, 10, 40, 12, 40, 782, 9, 40, 1, 40, 1, 40, 4, 40, 786, 8, 40, 11, 40, 12, 40, 787, 1, 40, 4, 40, 791, 8, 40, 11, 40, 12, 40, 792, 1, 40, 1, 40, 5, 40, 797, 8, 40, 10, 40, 12, 40, 800, 9, 40, 3, 40, 802, 8, 40, 1, 40, 1, 40, 1, 40, 1, 40, 4, 40, 808, 8, 40, 11, 40, 12, 40, 809, 1, 40, 1, 40, 3, 40, 814, 8, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 75, 1, 75, 1, 76, 1, 76, 1, 77, 1, 77, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 3, 80, 946, 8, 80, 1, 80, 5, 80, 949, 8, 80, 10, 80, 12, 80, 952, 9, 80, 1, 80, 1, 80, 4, 80, 956, 8, 80, 11, 80, 12, 80, 957, 3, 80, 960, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 5, 83, 974, 8, 83, 10, 83, 12, 83, 977, 9, 83, 1, 83, 1, 83, 3, 83, 981, 8, 83, 1, 83, 4, 83, 984, 8, 83, 11, 83, 12, 83, 985, 3, 83, 988, 8, 83, 1, 84, 1, 84, 4, 84, 992, 8, 84, 11, 84, 12, 84, 993, 1, 84, 1, 84, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 3, 101, 1071, 8, 101, 1, 102, 4, 102, 1074, 8, 102, 11, 102, 12, 102, 1075, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 3, 113, 1125, 8, 113, 1, 114, 1, 114, 3, 114, 1129, 8, 114, 1, 114, 5, 114, 1132, 8, 114, 10, 114, 12, 114, 1135, 9, 114, 1, 114, 1, 114, 3, 114, 1139, 8, 114, 1, 114, 4, 114, 1142, 8, 114, 11, 114, 12, 114, 1143, 3, 114, 1146, 8, 114, 1, 115, 1, 115, 4, 115, 1150, 8, 115, 11, 115, 12, 115, 1151, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 135, 4, 135, 1237, 8, 135, 11, 135, 12, 135, 1238, 1, 135, 1, 135, 3, 135, 1243, 8, 135, 1, 135, 4, 135, 1246, 8, 135, 11, 135, 12, 135, 1247, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 1, 147, 1, 147, 1, 147, 1, 148, 1, 148, 1, 148, 1, 148, 1, 148, 1, 149, 1, 149, 1, 149, 1, 149, 1, 150, 1, 150, 1, 150, 1, 150, 1, 151, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 1, 164, 1, 164, 1, 165, 1, 165, 1, 165, 1, 165, 1, 166, 1, 166, 1, 166, 1, 166, 1, 166, 1, 167, 1, 167, 1, 167, 1, 167, 1, 168, 1, 168, 1, 168, 1, 168, 1, 168, 4, 168, 1393, 8, 168, 11, 168, 12, 168, 1394, 1, 169, 1, 169, 1, 169, 1, 169, 1, 170, 1, 170, 1, 170, 1, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 175, 1, 175, 1, 176, 1, 176, 1, 176, 1, 176, 1, 176, 1, 177, 1, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 179, 1, 179, 1, 180, 1, 180, 1, 180, 1, 180, 1, 181, 1, 181, 1, 181, 1, 181, 1, 182, 1, 182, 1, 182, 1, 182, 1, 182, 1, 182, 1, 183, 1, 183, 1, 183, 1, 183, 1, 184, 1, 184, 1, 184, 1, 184, 1, 185, 1, 185, 1, 185, 1, 185, 1, 186, 1, 186, 1, 186, 1, 186, 1, 187, 1, 187, 1, 187, 1, 187, 1, 188, 1, 188, 1, 188, 1, 188, 1, 189, 1, 189, 1, 189, 1, 189, 1, 189, 1, 190, 1, 190, 1, 190, 1, 190, 1, 190, 1, 191, 1, 191, 1, 191, 1, 191, 1, 192, 1, 192, 1, 192, 1, 192, 1, 192, 1, 192, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 193, 1, 194, 1, 194, 1, 194, 1, 194, 1, 195, 1, 195, 1, 195, 1, 195, 1, 196, 1, 196, 1, 196, 1, 196, 1, 197, 1, 197, 1, 197, 1, 197, 1, 198, 1, 198, 1, 198, 1, 198, 1, 199, 1, 199, 1, 199, 1, 199, 1, 200, 1, 200, 1, 200, 1, 200, 1, 201, 1, 201, 1, 201, 1, 201, 1, 202, 1, 202, 1, 202, 1, 202, 1, 202, 1, 203, 1, 203, 1, 203, 1, 203, 1, 203, 1, 203, 1, 204, 1, 204, 1, 204, 1, 204, 1, 204, 1, 204, 1, 205, 1, 205, 1, 205, 1, 205, 1, 206, 1, 206, 1, 206, 1, 206, 1, 207, 1, 207, 1, 207, 1, 207, 1, 208, 1, 208, 1, 208, 1, 208, 1, 208, 1, 208, 1, 209, 1, 209, 1, 209, 1, 209, 1, 209, 1, 209, 1, 210, 1, 210, 1, 210, 1, 210, 1, 211, 1, 211, 1, 211, 1, 211, 1, 212, 1, 212, 1, 212, 1, 212, 1, 213, 1, 213, 1, 213, 1, 213, 1, 213, 1, 213, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 1, 214, 1, 215, 1, 215, 1, 215, 1, 215, 1, 215, 1, 215, 1, 216, 1, 216, 1, 216, 1, 216, 1, 216, 2, 682, 751, 0, 217, 16, 1, 18, 2, 20, 3, 22, 4, 24, 5, 26, 6, 28, 7, 30, 8, 32, 9, 34, 10, 36, 11, 38, 12, 40, 13, 42, 14, 44, 15, 46, 16, 48, 17, 50, 18, 52, 19, 54, 20, 56, 21, 58, 22, 60, 23, 62, 24, 64, 25, 66, 26, 68, 27, 70, 28, 72, 0, 74, 0, 76, 0, 78, 0, 80, 0, 82, 0, 84, 0, 86, 0, 88, 0, 90, 0, 92, 29, 94, 30, 96, 31, 98, 32, 100, 33, 102, 34, 104, 35, 106, 36, 108, 37, 110, 38, 112, 39, 114, 40, 116, 41, 118, 42, 120, 43, 122, 44, 124, 45, 126, 46, 128, 47, 130, 48, 132, 49, 134, 50, 136, 51, 138, 52, 140, 53, 142, 54, 144, 55, 146, 56, 148, 57, 150, 58, 152, 59, 154, 60, 156, 61, 158, 62, 160, 63, 162, 64, 164, 65, 166, 66, 168, 67, 170, 68, 172, 69, 174, 0, 176, 70, 178, 71, 180, 72, 182, 73, 184, 0, 186, 74, 188, 75, 190, 76, 192, 77, 194, 0, 196, 0, 198, 78, 200, 79, 202, 80, 204, 0, 206, 0, 208, 0, 210, 0, 212, 0, 214, 0, 216, 81, 218, 0, 220, 82, 222, 0, 224, 0, 226, 83, 228, 84, 230, 85, 232, 0, 234, 0, 236, 0, 238, 0, 240, 0, 242, 0, 244, 0, 246, 86, 248, 87, 250, 88, 252, 89, 254, 0, 256, 0, 258, 0, 260, 0, 262, 0, 264, 0, 266, 90, 268, 0, 270, 91, 272, 92, 274, 93, 276, 0, 278, 0, 280, 94, 282, 95, 284, 0, 286, 96, 288, 0, 290, 97, 292, 98, 294, 99, 296, 0, 298, 0, 300, 0, 302, 0, 304, 0, 306, 0, 308, 0, 310, 0, 312, 0, 314, 100, 316, 101, 318, 102, 320, 0, 322, 0, 324, 0, 326, 0, 328, 0, 330, 0, 332, 103, 334, 104, 336, 105, 338, 0, 340, 106, 342, 107, 344, 108, 346, 109, 348, 0, 350, 0, 352, 110, 354, 111, 356, 112, 358, 113, 360, 0, 362, 0, 364, 0, 366, 0, 368, 0, 370, 0, 372, 0, 374, 114, 376, 115, 378, 116, 380, 0, 382, 0, 384, 0, 386, 0, 388, 117, 390, 118, 392, 119, 394, 0, 396, 120, 398, 0, 400, 0, 402, 121, 404, 0, 406, 0, 408, 0, 410, 0, 412, 0, 414, 122, 416, 123, 418, 124, 420, 0, 422, 0, 424, 0, 426, 125, 428, 126, 430, 127, 432, 0, 434, 0, 436, 128, 438, 129, 440, 130, 442, 0, 444, 0, 446, 0, 448, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 36, 2, 0, 68, 68, 100, 100, 2, 0, 73, 73, 105, 105, 2, 0, 83, 83, 115, 115, 2, 0, 69, 69, 101, 101, 2, 0, 67, 67, 99, 99, 2, 0, 84, 84, 116, 116, 2, 0, 82, 82, 114, 114, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 78, 78, 110, 110, 2, 0, 72, 72, 104, 104, 2, 0, 86, 86, 118, 118, 2, 0, 65, 65, 97, 97, 2, 0, 76, 76, 108, 108, 2, 0, 88, 88, 120, 120, 2, 0, 70, 70, 102, 102, 2, 0, 77, 77, 109, 109, 2, 0, 71, 71, 103, 103, 2, 0, 75, 75, 107, 107, 2, 0, 87, 87, 119, 119, 2, 0, 85, 85, 117, 117, 6, 0, 9, 10, 13, 13, 32, 32, 47, 47, 91, 91, 93, 93, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 2, 0, 65, 90, 97, 122, 8, 0, 34, 34, 78, 78, 82, 82, 84, 84, 92, 92, 110, 110, 114, 114, 116, 116, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 43, 43, 45, 45, 1, 0, 96, 96, 2, 0, 66, 66, 98, 98, 2, 0, 89, 89, 121, 121, 11, 0, 9, 10, 13, 13, 32, 32, 34, 34, 44, 44, 47, 47, 58, 58, 61, 61, 91, 91, 93, 93, 124, 124, 2, 0, 42, 42, 47, 47, 11, 0, 9, 10, 13, 13, 32, 32, 34, 35, 44, 44, 47, 47, 58, 58, 60, 60, 62, 63, 92, 92, 124, 124, 2, 0, 74, 74, 106, 106, 1644, 0, 16, 1, 0, 0, 0, 0, 18, 1, 0, 0, 0, 0, 20, 1, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 24, 1, 0, 0, 0, 0, 26, 1, 0, 0, 0, 0, 28, 1, 0, 0, 0, 0, 30, 1, 0, 0, 0, 0, 32, 1, 0, 0, 0, 0, 34, 1, 0, 0, 0, 0, 36, 1, 0, 0, 0, 0, 38, 1, 0, 0, 0, 0, 40, 1, 0, 0, 0, 0, 42, 1, 0, 0, 0, 0, 44, 1, 0, 0, 0, 0, 46, 1, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 50, 1, 0, 0, 0, 0, 52, 1, 0, 0, 0, 0, 54, 1, 0, 0, 0, 0, 56, 1, 0, 0, 0, 0, 58, 1, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 62, 1, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 66, 1, 0, 0, 0, 0, 68, 1, 0, 0, 0, 1, 70, 1, 0, 0, 0, 1, 92, 1, 0, 0, 0, 1, 94, 1, 0, 0, 0, 1, 96, 1, 0, 0, 0, 1, 98, 1, 0, 0, 0, 1, 100, 1, 0, 0, 0, 1, 102, 1, 0, 0, 0, 1, 104, 1, 0, 0, 0, 1, 106, 1, 0, 0, 0, 1, 108, 1, 0, 0, 0, 1, 110, 1, 0, 0, 0, 1, 112, 1, 0, 0, 0, 1, 114, 1, 0, 0, 0, 1, 116, 1, 0, 0, 0, 1, 118, 1, 0, 0, 0, 1, 120, 1, 0, 0, 0, 1, 122, 1, 0, 0, 0, 1, 124, 1, 0, 0, 0, 1, 126, 1, 0, 0, 0, 1, 128, 1, 0, 0, 0, 1, 130, 1, 0, 0, 0, 1, 132, 1, 0, 0, 0, 1, 134, 1, 0, 0, 0, 1, 136, 1, 0, 0, 0, 1, 138, 1, 0, 0, 0, 1, 140, 1, 0, 0, 0, 1, 142, 1, 0, 0, 0, 1, 144, 1, 0, 0, 0, 1, 146, 1, 0, 0, 0, 1, 148, 1, 0, 0, 0, 1, 150, 1, 0, 0, 0, 1, 152, 1, 0, 0, 0, 1, 154, 1, 0, 0, 0, 1, 156, 1, 0, 0, 0, 1, 158, 1, 0, 0, 0, 1, 160, 1, 0, 0, 0, 1, 162, 1, 0, 0, 0, 1, 164, 1, 0, 0, 0, 1, 166, 1, 0, 0, 0, 1, 168, 1, 0, 0, 0, 1, 170, 1, 0, 0, 0, 1, 172, 1, 0, 0, 0, 1, 174, 1, 0, 0, 0, 1, 176, 1, 0, 0, 0, 1, 178, 1, 0, 0, 0, 1, 180, 1, 0, 0, 0, 1, 182, 1, 0, 0, 0, 1, 186, 1, 0, 0, 0, 1, 188, 1, 0, 0, 0, 1, 190, 1, 0, 0, 0, 1, 192, 1, 0, 0, 0, 2, 194, 1, 0, 0, 0, 2, 196, 1, 0, 0, 0, 2, 198, 1, 0, 0, 0, 2, 200, 1, 0, 0, 0, 2, 202, 1, 0, 0, 0, 3, 204, 1, 0, 0, 0, 3, 206, 1, 0, 0, 0, 3, 208, 1, 0, 0, 0, 3, 210, 1, 0, 0, 0, 3, 212, 1, 0, 0, 0, 3, 214, 1, 0, 0, 0, 3, 216, 1, 0, 0, 0, 3, 220, 1, 0, 0, 0, 3, 222, 1, 0, 0, 0, 3, 224, 1, 0, 0, 0, 3, 226, 1, 0, 0, 0, 3, 228, 1, 0, 0, 0, 3, 230, 1, 0, 0, 0, 4, 232, 1, 0, 0, 0, 4, 234, 1, 0, 0, 0, 4, 236, 1, 0, 0, 0, 4, 238, 1, 0, 0, 0, 4, 240, 1, 0, 0, 0, 4, 246, 1, 0, 0, 0, 4, 248, 1, 0, 0, 0, 4, 250, 1, 0, 0, 0, 4, 252, 1, 0, 0, 0, 5, 254, 1, 0, 0, 0, 5, 256, 1, 0, 0, 0, 5, 258, 1, 0, 0, 0, 5, 260, 1, 0, 0, 0, 5, 262, 1, 0, 0, 0, 5, 264, 1, 0, 0, 0, 5, 266, 1, 0, 0, 0, 5, 268, 1, 0, 0, 0, 5, 270, 1, 0, 0, 0, 5, 272, 1, 0, 0, 0, 5, 274, 1, 0, 0, 0, 6, 276, 1, 0, 0, 0, 6, 278, 1, 0, 0, 0, 6, 280, 1, 0, 0, 0, 6, 282, 1, 0, 0, 0, 6, 286, 1, 0, 0, 0, 6, 288, 1, 0, 0, 0, 6, 290, 1, 0, 0, 0, 6, 292, 1, 0, 0, 0, 6, 294, 1, 0, 0, 0, 7, 296, 1, 0, 0, 0, 7, 298, 1, 0, 0, 0, 7, 300, 1, 0, 0, 0, 7, 302, 1, 0, 0, 0, 7, 304, 1, 0, 0, 0, 7, 306, 1, 0, 0, 0, 7, 308, 1, 0, 0, 0, 7, 310, 1, 0, 0, 0, 7, 312, 1, 0, 0, 0, 7, 314, 1, 0, 0, 0, 7, 316, 1, 0, 0, 0, 7, 318, 1, 0, 0, 0, 8, 320, 1, 0, 0, 0, 8, 322, 1, 0, 0, 0, 8, 324, 1, 0, 0, 0, 8, 326, 1, 0, 0, 0, 8, 328, 1, 0, 0, 0, 8, 330, 1, 0, 0, 0, 8, 332, 1, 0, 0, 0, 8, 334, 1, 0, 0, 0, 8, 336, 1, 0, 0, 0, 9, 338, 1, 0, 0, 0, 9, 340, 1, 0, 0, 0, 9, 342, 1, 0, 0, 0, 9, 344, 1, 0, 0, 0, 9, 346, 1, 0, 0, 0, 10, 348, 1, 0, 0, 0, 10, 350, 1, 0, 0, 0, 10, 352, 1, 0, 0, 0, 10, 354, 1, 0, 0, 0, 10, 356, 1, 0, 0, 0, 10, 358, 1, 0, 0, 0, 11, 360, 1, 0, 0, 0, 11, 362, 1, 0, 0, 0, 11, 364, 1, 0, 0, 0, 11, 366, 1, 0, 0, 0, 11, 368, 1, 0, 0, 0, 11, 370, 1, 0, 0, 0, 11, 372, 1, 0, 0, 0, 11, 374, 1, 0, 0, 0, 11, 376, 1, 0, 0, 0, 11, 378, 1, 0, 0, 0, 12, 380, 1, 0, 0, 0, 12, 382, 1, 0, 0, 0, 12, 384, 1, 0, 0, 0, 12, 386, 1, 0, 0, 0, 12, 388, 1, 0, 0, 0, 12, 390, 1, 0, 0, 0, 12, 392, 1, 0, 0, 0, 13, 394, 1, 0, 0, 0, 13, 396, 1, 0, 0, 0, 13, 398, 1, 0, 0, 0, 13, 400, 1, 0, 0, 0, 13, 402, 1, 0, 0, 0, 13, 404, 1, 0, 0, 0, 13, 406, 1, 0, 0, 0, 13, 408, 1, 0, 0, 0, 13, 410, 1, 0, 0, 0, 13, 412, 1, 0, 0, 0, 13, 414, 1, 0, 0, 0, 13, 416, 1, 0, 0, 0, 13, 418, 1, 0, 0, 0, 14, 420, 1, 0, 0, 0, 14, 422, 1, 0, 0, 0, 14, 424, 1, 0, 0, 0, 14, 426, 1, 0, 0, 0, 14, 428, 1, 0, 0, 0, 14, 430, 1, 0, 0, 0, 15, 432, 1, 0, 0, 0, 15, 434, 1, 0, 0, 0, 15, 436, 1, 0, 0, 0, 15, 438, 1, 0, 0, 0, 15, 440, 1, 0, 0, 0, 15, 442, 1, 0, 0, 0, 15, 444, 1, 0, 0, 0, 15, 446, 1, 0, 0, 0, 15, 448, 1, 0, 0, 0, 16, 450, 1, 0, 0, 0, 18, 460, 1, 0, 0, 0, 20, 467, 1, 0, 0, 0, 22, 476, 1, 0, 0, 0, 24, 483, 1, 0, 0, 0, 26, 493, 1, 0, 0, 0, 28, 500, 1, 0, 0, 0, 30, 507, 1, 0, 0, 0, 32, 514, 1, 0, 0, 0, 34, 522, 1, 0, 0, 0, 36, 534, 1, 0, 0, 0, 38, 543, 1, 0, 0, 0, 40, 549, 1, 0, 0, 0, 42, 556, 1, 0, 0, 0, 44, 563, 1, 0, 0, 0, 46, 571, 1, 0, 0, 0, 48, 579, 1, 0, 0, 0, 50, 588, 1, 0, 0, 0, 52, 603, 1, 0, 0, 0, 54, 615, 1, 0, 0, 0, 56, 626, 1, 0, 0, 0, 58, 634, 1, 0, 0, 0, 60, 642, 1, 0, 0, 0, 62, 652, 1, 0, 0, 0, 64, 658, 1, 0, 0, 0, 66, 675, 1, 0, 0, 0, 68, 691, 1, 0, 0, 0, 70, 697, 1, 0, 0, 0, 72, 701, 1, 0, 0, 0, 74, 703, 1, 0, 0, 0, 76, 705, 1, 0, 0, 0, 78, 708, 1, 0, 0, 0, 80, 710, 1, 0, 0, 0, 82, 719, 1, 0, 0, 0, 84, 721, 1, 0, 0, 0, 86, 726, 1, 0, 0, 0, 88, 728, 1, 0, 0, 0, 90, 733, 1, 0, 0, 0, 92, 764, 1, 0, 0, 0, 94, 767, 1, 0, 0, 0, 96, 813, 1, 0, 0, 0, 98, 815, 1, 0, 0, 0, 100, 818, 1, 0, 0, 0, 102, 822, 1, 0, 0, 0, 104, 826, 1, 0, 0, 0, 106, 828, 1, 0, 0, 0, 108, 831, 1, 0, 0, 0, 110, 833, 1, 0, 0, 0, 112, 835, 1, 0, 0, 0, 114, 840, 1, 0, 0, 0, 116, 842, 1, 0, 0, 0, 118, 848, 1, 0, 0, 0, 120, 854, 1, 0, 0, 0, 122, 857, 1, 0, 0, 0, 124, 860, 1, 0, 0, 0, 126, 865, 1, 0, 0, 0, 128, 870, 1, 0, 0, 0, 130, 872, 1, 0, 0, 0, 132, 876, 1, 0, 0, 0, 134, 881, 1, 0, 0, 0, 136, 887, 1, 0, 0, 0, 138, 890, 1, 0, 0, 0, 140, 892, 1, 0, 0, 0, 142, 898, 1, 0, 0, 0, 144, 900, 1, 0, 0, 0, 146, 905, 1, 0, 0, 0, 148, 908, 1, 0, 0, 0, 150, 911, 1, 0, 0, 0, 152, 914, 1, 0, 0, 0, 154, 916, 1, 0, 0, 0, 156, 919, 1, 0, 0, 0, 158, 921, 1, 0, 0, 0, 160, 924, 1, 0, 0, 0, 162, 926, 1, 0, 0, 0, 164, 928, 1, 0, 0, 0, 166, 930, 1, 0, 0, 0, 168, 932, 1, 0, 0, 0, 170, 934, 1, 0, 0, 0, 172, 936, 1, 0, 0, 0, 174, 938, 1, 0, 0, 0, 176, 959, 1, 0, 0, 0, 178, 961, 1, 0, 0, 0, 180, 966, 1, 0, 0, 0, 182, 987, 1, 0, 0, 0, 184, 989, 1, 0, 0, 0, 186, 997, 1, 0, 0, 0, 188, 999, 1, 0, 0, 0, 190, 1003, 1, 0, 0, 0, 192, 1007, 1, 0, 0, 0, 194, 1011, 1, 0, 0, 0, 196, 1016, 1, 0, 0, 0, 198, 1021, 1, 0, 0, 0, 200, 1025, 1, 0, 0, 0, 202, 1029, 1, 0, 0, 0, 204, 1033, 1, 0, 0, 0, 206, 1038, 1, 0, 0, 0, 208, 1042, 1, 0, 0, 0, 210, 1046, 1, 0, 0, 0, 212, 1050, 1, 0, 0, 0, 214, 1054, 1, 0, 0, 0, 216, 1058, 1, 0, 0, 0, 218, 1070, 1, 0, 0, 0, 220, 1073, 1, 0, 0, 0, 222, 1077, 1, 0, 0, 0, 224, 1081, 1, 0, 0, 0, 226, 1085, 1, 0, 0, 0, 228, 1089, 1, 0, 0, 0, 230, 1093, 1, 0, 0, 0, 232, 1097, 1, 0, 0, 0, 234, 1102, 1, 0, 0, 0, 236, 1106, 1, 0, 0, 0, 238, 1110, 1, 0, 0, 0, 240, 1115, 1, 0, 0, 0, 242, 1124, 1, 0, 0, 0, 244, 1145, 1, 0, 0, 0, 246, 1149, 1, 0, 0, 0, 248, 1153, 1, 0, 0, 0, 250, 1157, 1, 0, 0, 0, 252, 1161, 1, 0, 0, 0, 254, 1165, 1, 0, 0, 0, 256, 1170, 1, 0, 0, 0, 258, 1174, 1, 0, 0, 0, 260, 1178, 1, 0, 0, 0, 262, 1182, 1, 0, 0, 0, 264, 1187, 1, 0, 0, 0, 266, 1192, 1, 0, 0, 0, 268, 1195, 1, 0, 0, 0, 270, 1199, 1, 0, 0, 0, 272, 1203, 1, 0, 0, 0, 274, 1207, 1, 0, 0, 0, 276, 1211, 1, 0, 0, 0, 278, 1216, 1, 0, 0, 0, 280, 1221, 1, 0, 0, 0, 282, 1226, 1, 0, 0, 0, 284, 1233, 1, 0, 0, 0, 286, 1242, 1, 0, 0, 0, 288, 1249, 1, 0, 0, 0, 290, 1253, 1, 0, 0, 0, 292, 1257, 1, 0, 0, 0, 294, 1261, 1, 0, 0, 0, 296, 1265, 1, 0, 0, 0, 298, 1271, 1, 0, 0, 0, 300, 1275, 1, 0, 0, 0, 302, 1279, 1, 0, 0, 0, 304, 1283, 1, 0, 0, 0, 306, 1287, 1, 0, 0, 0, 308, 1291, 1, 0, 0, 0, 310, 1295, 1, 0, 0, 0, 312, 1300, 1, 0, 0, 0, 314, 1305, 1, 0, 0, 0, 316, 1309, 1, 0, 0, 0, 318, 1313, 1, 0, 0, 0, 320, 1317, 1, 0, 0, 0, 322, 1322, 1, 0, 0, 0, 324, 1326, 1, 0, 0, 0, 326, 1331, 1, 0, 0, 0, 328, 1336, 1, 0, 0, 0, 330, 1340, 1, 0, 0, 0, 332, 1344, 1, 0, 0, 0, 334, 1348, 1, 0, 0, 0, 336, 1352, 1, 0, 0, 0, 338, 1356, 1, 0, 0, 0, 340, 1361, 1, 0, 0, 0, 342, 1366, 1, 0, 0, 0, 344, 1370, 1, 0, 0, 0, 346, 1374, 1, 0, 0, 0, 348, 1378, 1, 0, 0, 0, 350, 1383, 1, 0, 0, 0, 352, 1392, 1, 0, 0, 0, 354, 1396, 1, 0, 0, 0, 356, 1400, 1, 0, 0, 0, 358, 1404, 1, 0, 0, 0, 360, 1408, 1, 0, 0, 0, 362, 1413, 1, 0, 0, 0, 364, 1417, 1, 0, 0, 0, 366, 1421, 1, 0, 0, 0, 368, 1425, 1, 0, 0, 0, 370, 1430, 1, 0, 0, 0, 372, 1434, 1, 0, 0, 0, 374, 1438, 1, 0, 0, 0, 376, 1442, 1, 0, 0, 0, 378, 1446, 1, 0, 0, 0, 380, 1450, 1, 0, 0, 0, 382, 1456, 1, 0, 0, 0, 384, 1460, 1, 0, 0, 0, 386, 1464, 1, 0, 0, 0, 388, 1468, 1, 0, 0, 0, 390, 1472, 1, 0, 0, 0, 392, 1476, 1, 0, 0, 0, 394, 1480, 1, 0, 0, 0, 396, 1485, 1, 0, 0, 0, 398, 1490, 1, 0, 0, 0, 400, 1494, 1, 0, 0, 0, 402, 1500, 1, 0, 0, 0, 404, 1509, 1, 0, 0, 0, 406, 1513, 1, 0, 0, 0, 408, 1517, 1, 0, 0, 0, 410, 1521, 1, 0, 0, 0, 412, 1525, 1, 0, 0, 0, 414, 1529, 1, 0, 0, 0, 416, 1533, 1, 0, 0, 0, 418, 1537, 1, 0, 0, 0, 420, 1541, 1, 0, 0, 0, 422, 1546, 1, 0, 0, 0, 424, 1552, 1, 0, 0, 0, 426, 1558, 1, 0, 0, 0, 428, 1562, 1, 0, 0, 0, 430, 1566, 1, 0, 0, 0, 432, 1570, 1, 0, 0, 0, 434, 1576, 1, 0, 0, 0, 436, 1582, 1, 0, 0, 0, 438, 1586, 1, 0, 0, 0, 440, 1590, 1, 0, 0, 0, 442, 1594, 1, 0, 0, 0, 444, 1600, 1, 0, 0, 0, 446, 1606, 1, 0, 0, 0, 448, 1612, 1, 0, 0, 0, 450, 451, 7, 0, 0, 0, 451, 452, 7, 1, 0, 0, 452, 453, 7, 2, 0, 0, 453, 454, 7, 2, 0, 0, 454, 455, 7, 3, 0, 0, 455, 456, 7, 4, 0, 0, 456, 457, 7, 5, 0, 0, 457, 458, 1, 0, 0, 0, 458, 459, 6, 0, 0, 0, 459, 17, 1, 0, 0, 0, 460, 461, 7, 0, 0, 0, 461, 462, 7, 6, 0, 0, 462, 463, 7, 7, 0, 0, 463, 464, 7, 8, 0, 0, 464, 465, 1, 0, 0, 0, 465, 466, 6, 1, 1, 0, 466, 19, 1, 0, 0, 0, 467, 468, 7, 3, 0, 0, 468, 469, 7, 9, 0, 0, 469, 470, 7, 6, 0, 0, 470, 471, 7, 1, 0, 0, 471, 472, 7, 4, 0, 0, 472, 473, 7, 10, 0, 0, 473, 474, 1, 0, 0, 0, 474, 475, 6, 2, 2, 0, 475, 21, 1, 0, 0, 0, 476, 477, 7, 3, 0, 0, 477, 478, 7, 11, 0, 0, 478, 479, 7, 12, 0, 0, 479, 480, 7, 13, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 6, 3, 0, 0, 482, 23, 1, 0, 0, 0, 483, 484, 7, 3, 0, 0, 484, 485, 7, 14, 0, 0, 485, 486, 7, 8, 0, 0, 486, 487, 7, 13, 0, 0, 487, 488, 7, 12, 0, 0, 488, 489, 7, 1, 0, 0, 489, 490, 7, 9, 0, 0, 490, 491, 1, 0, 0, 0, 491, 492, 6, 4, 3, 0, 492, 25, 1, 0, 0, 0, 493, 494, 7, 15, 0, 0, 494, 495, 7, 6, 0, 0, 495, 496, 7, 7, 0, 0, 496, 497, 7, 16, 0, 0, 497, 498, 1, 0, 0, 0, 498, 499, 6, 5, 4, 0, 499, 27, 1, 0, 0, 0, 500, 501, 7, 17, 0, 0, 501, 502, 7, 6, 0, 0, 502, 503, 7, 7, 0, 0, 503, 504, 7, 18, 0, 0, 504, 505, 1, 0, 0, 0, 505, 506, 6, 6, 0, 0, 506, 29, 1, 0, 0, 0, 507, 508, 7, 18, 0, 0, 508, 509, 7, 3, 0, 0, 509, 510, 7, 3, 0, 0, 510, 511, 7, 8, 0, 0, 511, 512, 1, 0, 0, 0, 512, 513, 6, 7, 1, 0, 513, 31, 1, 0, 0, 0, 514, 515, 7, 13, 0, 0, 515, 516, 7, 1, 0, 0, 516, 517, 7, 16, 0, 0, 517, 518, 7, 1, 0, 0, 518, 519, 7, 5, 0, 0, 519, 520, 1, 0, 0, 0, 520, 521, 6, 8, 0, 0, 521, 33, 1, 0, 0, 0, 522, 523, 7, 16, 0, 0, 523, 524, 7, 11, 0, 0, 524, 525, 5, 95, 0, 0, 525, 526, 7, 3, 0, 0, 526, 527, 7, 14, 0, 0, 527, 528, 7, 8, 0, 0, 528, 529, 7, 12, 0, 0, 529, 530, 7, 9, 0, 0, 530, 531, 7, 0, 0, 0, 531, 532, 1, 0, 0, 0, 532, 533, 6, 9, 5, 0, 533, 35, 1, 0, 0, 0, 534, 535, 7, 6, 0, 0, 535, 536, 7, 3, 0, 0, 536, 537, 7, 9, 0, 0, 537, 538, 7, 12, 0, 0, 538, 539, 7, 16, 0, 0, 539, 540, 7, 3, 0, 0, 540, 541, 1, 0, 0, 0, 541, 542, 6, 10, 6, 0, 542, 37, 1, 0, 0, 0, 543, 544, 7, 6, 0, 0, 544, 545, 7, 7, 0, 0, 545, 546, 7, 19, 0, 0, 546, 547, 1, 0, 0, 0, 547, 548, 6, 11, 0, 0, 548, 39, 1, 0, 0, 0, 549, 550, 7, 2, 0, 0, 550, 551, 7, 10, 0, 0, 551, 552, 7, 7, 0, 0, 552, 553, 7, 19, 0, 0, 553, 554, 1, 0, 0, 0, 554, 555, 6, 12, 7, 0, 555, 41, 1, 0, 0, 0, 556, 557, 7, 2, 0, 0, 557, 558, 7, 7, 0, 0, 558, 559, 7, 6, 0, 0, 559, 560, 7, 5, 0, 0, 560, 561, 1, 0, 0, 0, 561, 562, 6, 13, 0, 0, 562, 43, 1, 0, 0, 0, 563, 564, 7, 2, 0, 0, 564, 565, 7, 5, 0, 0, 565, 566, 7, 12, 0, 0, 566, 567, 7, 5, 0, 0, 567, 568, 7, 2, 0, 0, 568, 569, 1, 0, 0, 0, 569, 570, 6, 14, 0, 0, 570, 45, 1, 0, 0, 0, 571, 572, 7, 19, 0, 0, 572, 573, 7, 10, 0, 0, 573, 574, 7, 3, 0, 0, 574, 575, 7, 6, 0, 0, 575, 576, 7, 3, 0, 0, 576, 577, 1, 0, 0, 0, 577, 578, 6, 15, 0, 0, 578, 47, 1, 0, 0, 0, 579, 580, 7, 13, 0, 0, 580, 581, 7, 7, 0, 0, 581, 582, 7, 7, 0, 0, 582, 583, 7, 18, 0, 0, 583, 584, 7, 20, 0, 0, 584, 585, 7, 8, 0, 0, 585, 586, 1, 0, 0, 0, 586, 587, 6, 16, 8, 0, 587, 49, 1, 0, 0, 0, 588, 589, 4, 17, 0, 0, 589, 590, 7, 1, 0, 0, 590, 591, 7, 9, 0, 0, 591, 592, 7, 13, 0, 0, 592, 593, 7, 1, 0, 0, 593, 594, 7, 9, 0, 0, 594, 595, 7, 3, 0, 0, 595, 596, 7, 2, 0, 0, 596, 597, 7, 5, 0, 0, 597, 598, 7, 12, 0, 0, 598, 599, 7, 5, 0, 0, 599, 600, 7, 2, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, 6, 17, 0, 0, 602, 51, 1, 0, 0, 0, 603, 604, 4, 18, 1, 0, 604, 605, 7, 13, 0, 0, 605, 606, 7, 7, 0, 0, 606, 607, 7, 7, 0, 0, 607, 608, 7, 18, 0, 0, 608, 609, 7, 20, 0, 0, 609, 610, 7, 8, 0, 0, 610, 611, 5, 95, 0, 0, 611, 612, 5, 128020, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 6, 18, 9, 0, 614, 53, 1, 0, 0, 0, 615, 616, 4, 19, 2, 0, 616, 617, 7, 16, 0, 0, 617, 618, 7, 3, 0, 0, 618, 619, 7, 5, 0, 0, 619, 620, 7, 6, 0, 0, 620, 621, 7, 1, 0, 0, 621, 622, 7, 4, 0, 0, 622, 623, 7, 2, 0, 0, 623, 624, 1, 0, 0, 0, 624, 625, 6, 19, 10, 0, 625, 55, 1, 0, 0, 0, 626, 627, 4, 20, 3, 0, 627, 628, 7, 15, 0, 0, 628, 629, 7, 20, 0, 0, 629, 630, 7, 13, 0, 0, 630, 631, 7, 13, 0, 0, 631, 632, 1, 0, 0, 0, 632, 633, 6, 20, 8, 0, 633, 57, 1, 0, 0, 0, 634, 635, 4, 21, 4, 0, 635, 636, 7, 13, 0, 0, 636, 637, 7, 3, 0, 0, 637, 638, 7, 15, 0, 0, 638, 639, 7, 5, 0, 0, 639, 640, 1, 0, 0, 0, 640, 641, 6, 21, 8, 0, 641, 59, 1, 0, 0, 0, 642, 643, 4, 22, 5, 0, 643, 644, 7, 6, 0, 0, 644, 645, 7, 1, 0, 0, 645, 646, 7, 17, 0, 0, 646, 647, 7, 10, 0, 0, 647, 648, 7, 5, 0, 0, 648, 649, 1, 0, 0, 0, 649, 650, 6, 22, 8, 0, 650, 61, 1, 0, 0, 0, 651, 653, 8, 21, 0, 0, 652, 651, 1, 0, 0, 0, 653, 654, 1, 0, 0, 0, 654, 652, 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 656, 1, 0, 0, 0, 656, 657, 6, 23, 0, 0, 657, 63, 1, 0, 0, 0, 658, 659, 5, 47, 0, 0, 659, 660, 5, 47, 0, 0, 660, 664, 1, 0, 0, 0, 661, 663, 8, 22, 0, 0, 662, 661, 1, 0, 0, 0, 663, 666, 1, 0, 0, 0, 664, 662, 1, 0, 0, 0, 664, 665, 1, 0, 0, 0, 665, 668, 1, 0, 0, 0, 666, 664, 1, 0, 0, 0, 667, 669, 5, 13, 0, 0, 668, 667, 1, 0, 0, 0, 668, 669, 1, 0, 0, 0, 669, 671, 1, 0, 0, 0, 670, 672, 5, 10, 0, 0, 671, 670, 1, 0, 0, 0, 671, 672, 1, 0, 0, 0, 672, 673, 1, 0, 0, 0, 673, 674, 6, 24, 11, 0, 674, 65, 1, 0, 0, 0, 675, 676, 5, 47, 0, 0, 676, 677, 5, 42, 0, 0, 677, 682, 1, 0, 0, 0, 678, 681, 3, 66, 25, 0, 679, 681, 9, 0, 0, 0, 680, 678, 1, 0, 0, 0, 680, 679, 1, 0, 0, 0, 681, 684, 1, 0, 0, 0, 682, 683, 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 685, 1, 0, 0, 0, 684, 682, 1, 0, 0, 0, 685, 686, 5, 42, 0, 0, 686, 687, 5, 47, 0, 0, 687, 688, 1, 0, 0, 0, 688, 689, 6, 25, 11, 0, 689, 67, 1, 0, 0, 0, 690, 692, 7, 23, 0, 0, 691, 690, 1, 0, 0, 0, 692, 693, 1, 0, 0, 0, 693, 691, 1, 0, 0, 0, 693, 694, 1, 0, 0, 0, 694, 695, 1, 0, 0, 0, 695, 696, 6, 26, 11, 0, 696, 69, 1, 0, 0, 0, 697, 698, 5, 124, 0, 0, 698, 699, 1, 0, 0, 0, 699, 700, 6, 27, 12, 0, 700, 71, 1, 0, 0, 0, 701, 702, 7, 24, 0, 0, 702, 73, 1, 0, 0, 0, 703, 704, 7, 25, 0, 0, 704, 75, 1, 0, 0, 0, 705, 706, 5, 92, 0, 0, 706, 707, 7, 26, 0, 0, 707, 77, 1, 0, 0, 0, 708, 709, 8, 27, 0, 0, 709, 79, 1, 0, 0, 0, 710, 712, 7, 3, 0, 0, 711, 713, 7, 28, 0, 0, 712, 711, 1, 0, 0, 0, 712, 713, 1, 0, 0, 0, 713, 715, 1, 0, 0, 0, 714, 716, 3, 72, 28, 0, 715, 714, 1, 0, 0, 0, 716, 717, 1, 0, 0, 0, 717, 715, 1, 0, 0, 0, 717, 718, 1, 0, 0, 0, 718, 81, 1, 0, 0, 0, 719, 720, 5, 64, 0, 0, 720, 83, 1, 0, 0, 0, 721, 722, 5, 96, 0, 0, 722, 85, 1, 0, 0, 0, 723, 727, 8, 29, 0, 0, 724, 725, 5, 96, 0, 0, 725, 727, 5, 96, 0, 0, 726, 723, 1, 0, 0, 0, 726, 724, 1, 0, 0, 0, 727, 87, 1, 0, 0, 0, 728, 729, 5, 95, 0, 0, 729, 89, 1, 0, 0, 0, 730, 734, 3, 74, 29, 0, 731, 734, 3, 72, 28, 0, 732, 734, 3, 88, 36, 0, 733, 730, 1, 0, 0, 0, 733, 731, 1, 0, 0, 0, 733, 732, 1, 0, 0, 0, 734, 91, 1, 0, 0, 0, 735, 740, 5, 34, 0, 0, 736, 739, 3, 76, 30, 0, 737, 739, 3, 78, 31, 0, 738, 736, 1, 0, 0, 0, 738, 737, 1, 0, 0, 0, 739, 742, 1, 0, 0, 0, 740, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 743, 1, 0, 0, 0, 742, 740, 1, 0, 0, 0, 743, 765, 5, 34, 0, 0, 744, 745, 5, 34, 0, 0, 745, 746, 5, 34, 0, 0, 746, 747, 5, 34, 0, 0, 747, 751, 1, 0, 0, 0, 748, 750, 8, 22, 0, 0, 749, 748, 1, 0, 0, 0, 750, 753, 1, 0, 0, 0, 751, 752, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 752, 754, 1, 0, 0, 0, 753, 751, 1, 0, 0, 0, 754, 755, 5, 34, 0, 0, 755, 756, 5, 34, 0, 0, 756, 757, 5, 34, 0, 0, 757, 759, 1, 0, 0, 0, 758, 760, 5, 34, 0, 0, 759, 758, 1, 0, 0, 0, 759, 760, 1, 0, 0, 0, 760, 762, 1, 0, 0, 0, 761, 763, 5, 34, 0, 0, 762, 761, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 735, 1, 0, 0, 0, 764, 744, 1, 0, 0, 0, 765, 93, 1, 0, 0, 0, 766, 768, 3, 72, 28, 0, 767, 766, 1, 0, 0, 0, 768, 769, 1, 0, 0, 0, 769, 767, 1, 0, 0, 0, 769, 770, 1, 0, 0, 0, 770, 95, 1, 0, 0, 0, 771, 773, 3, 72, 28, 0, 772, 771, 1, 0, 0, 0, 773, 774, 1, 0, 0, 0, 774, 772, 1, 0, 0, 0, 774, 775, 1, 0, 0, 0, 775, 776, 1, 0, 0, 0, 776, 780, 3, 114, 49, 0, 777, 779, 3, 72, 28, 0, 778, 777, 1, 0, 0, 0, 779, 782, 1, 0, 0, 0, 780, 778, 1, 0, 0, 0, 780, 781, 1, 0, 0, 0, 781, 814, 1, 0, 0, 0, 782, 780, 1, 0, 0, 0, 783, 785, 3, 114, 49, 0, 784, 786, 3, 72, 28, 0, 785, 784, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 785, 1, 0, 0, 0, 787, 788, 1, 0, 0, 0, 788, 814, 1, 0, 0, 0, 789, 791, 3, 72, 28, 0, 790, 789, 1, 0, 0, 0, 791, 792, 1, 0, 0, 0, 792, 790, 1, 0, 0, 0, 792, 793, 1, 0, 0, 0, 793, 801, 1, 0, 0, 0, 794, 798, 3, 114, 49, 0, 795, 797, 3, 72, 28, 0, 796, 795, 1, 0, 0, 0, 797, 800, 1, 0, 0, 0, 798, 796, 1, 0, 0, 0, 798, 799, 1, 0, 0, 0, 799, 802, 1, 0, 0, 0, 800, 798, 1, 0, 0, 0, 801, 794, 1, 0, 0, 0, 801, 802, 1, 0, 0, 0, 802, 803, 1, 0, 0, 0, 803, 804, 3, 80, 32, 0, 804, 814, 1, 0, 0, 0, 805, 807, 3, 114, 49, 0, 806, 808, 3, 72, 28, 0, 807, 806, 1, 0, 0, 0, 808, 809, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 811, 1, 0, 0, 0, 811, 812, 3, 80, 32, 0, 812, 814, 1, 0, 0, 0, 813, 772, 1, 0, 0, 0, 813, 783, 1, 0, 0, 0, 813, 790, 1, 0, 0, 0, 813, 805, 1, 0, 0, 0, 814, 97, 1, 0, 0, 0, 815, 816, 7, 30, 0, 0, 816, 817, 7, 31, 0, 0, 817, 99, 1, 0, 0, 0, 818, 819, 7, 12, 0, 0, 819, 820, 7, 9, 0, 0, 820, 821, 7, 0, 0, 0, 821, 101, 1, 0, 0, 0, 822, 823, 7, 12, 0, 0, 823, 824, 7, 2, 0, 0, 824, 825, 7, 4, 0, 0, 825, 103, 1, 0, 0, 0, 826, 827, 5, 61, 0, 0, 827, 105, 1, 0, 0, 0, 828, 829, 5, 58, 0, 0, 829, 830, 5, 58, 0, 0, 830, 107, 1, 0, 0, 0, 831, 832, 5, 58, 0, 0, 832, 109, 1, 0, 0, 0, 833, 834, 5, 44, 0, 0, 834, 111, 1, 0, 0, 0, 835, 836, 7, 0, 0, 0, 836, 837, 7, 3, 0, 0, 837, 838, 7, 2, 0, 0, 838, 839, 7, 4, 0, 0, 839, 113, 1, 0, 0, 0, 840, 841, 5, 46, 0, 0, 841, 115, 1, 0, 0, 0, 842, 843, 7, 15, 0, 0, 843, 844, 7, 12, 0, 0, 844, 845, 7, 13, 0, 0, 845, 846, 7, 2, 0, 0, 846, 847, 7, 3, 0, 0, 847, 117, 1, 0, 0, 0, 848, 849, 7, 15, 0, 0, 849, 850, 7, 1, 0, 0, 850, 851, 7, 6, 0, 0, 851, 852, 7, 2, 0, 0, 852, 853, 7, 5, 0, 0, 853, 119, 1, 0, 0, 0, 854, 855, 7, 1, 0, 0, 855, 856, 7, 9, 0, 0, 856, 121, 1, 0, 0, 0, 857, 858, 7, 1, 0, 0, 858, 859, 7, 2, 0, 0, 859, 123, 1, 0, 0, 0, 860, 861, 7, 13, 0, 0, 861, 862, 7, 12, 0, 0, 862, 863, 7, 2, 0, 0, 863, 864, 7, 5, 0, 0, 864, 125, 1, 0, 0, 0, 865, 866, 7, 13, 0, 0, 866, 867, 7, 1, 0, 0, 867, 868, 7, 18, 0, 0, 868, 869, 7, 3, 0, 0, 869, 127, 1, 0, 0, 0, 870, 871, 5, 40, 0, 0, 871, 129, 1, 0, 0, 0, 872, 873, 7, 9, 0, 0, 873, 874, 7, 7, 0, 0, 874, 875, 7, 5, 0, 0, 875, 131, 1, 0, 0, 0, 876, 877, 7, 9, 0, 0, 877, 878, 7, 20, 0, 0, 878, 879, 7, 13, 0, 0, 879, 880, 7, 13, 0, 0, 880, 133, 1, 0, 0, 0, 881, 882, 7, 9, 0, 0, 882, 883, 7, 20, 0, 0, 883, 884, 7, 13, 0, 0, 884, 885, 7, 13, 0, 0, 885, 886, 7, 2, 0, 0, 886, 135, 1, 0, 0, 0, 887, 888, 7, 7, 0, 0, 888, 889, 7, 6, 0, 0, 889, 137, 1, 0, 0, 0, 890, 891, 5, 63, 0, 0, 891, 139, 1, 0, 0, 0, 892, 893, 7, 6, 0, 0, 893, 894, 7, 13, 0, 0, 894, 895, 7, 1, 0, 0, 895, 896, 7, 18, 0, 0, 896, 897, 7, 3, 0, 0, 897, 141, 1, 0, 0, 0, 898, 899, 5, 41, 0, 0, 899, 143, 1, 0, 0, 0, 900, 901, 7, 5, 0, 0, 901, 902, 7, 6, 0, 0, 902, 903, 7, 20, 0, 0, 903, 904, 7, 3, 0, 0, 904, 145, 1, 0, 0, 0, 905, 906, 5, 61, 0, 0, 906, 907, 5, 61, 0, 0, 907, 147, 1, 0, 0, 0, 908, 909, 5, 61, 0, 0, 909, 910, 5, 126, 0, 0, 910, 149, 1, 0, 0, 0, 911, 912, 5, 33, 0, 0, 912, 913, 5, 61, 0, 0, 913, 151, 1, 0, 0, 0, 914, 915, 5, 60, 0, 0, 915, 153, 1, 0, 0, 0, 916, 917, 5, 60, 0, 0, 917, 918, 5, 61, 0, 0, 918, 155, 1, 0, 0, 0, 919, 920, 5, 62, 0, 0, 920, 157, 1, 0, 0, 0, 921, 922, 5, 62, 0, 0, 922, 923, 5, 61, 0, 0, 923, 159, 1, 0, 0, 0, 924, 925, 5, 43, 0, 0, 925, 161, 1, 0, 0, 0, 926, 927, 5, 45, 0, 0, 927, 163, 1, 0, 0, 0, 928, 929, 5, 42, 0, 0, 929, 165, 1, 0, 0, 0, 930, 931, 5, 47, 0, 0, 931, 167, 1, 0, 0, 0, 932, 933, 5, 37, 0, 0, 933, 169, 1, 0, 0, 0, 934, 935, 5, 123, 0, 0, 935, 171, 1, 0, 0, 0, 936, 937, 5, 125, 0, 0, 937, 173, 1, 0, 0, 0, 938, 939, 3, 46, 15, 0, 939, 940, 1, 0, 0, 0, 940, 941, 6, 79, 13, 0, 941, 175, 1, 0, 0, 0, 942, 945, 3, 138, 61, 0, 943, 946, 3, 74, 29, 0, 944, 946, 3, 88, 36, 0, 945, 943, 1, 0, 0, 0, 945, 944, 1, 0, 0, 0, 946, 950, 1, 0, 0, 0, 947, 949, 3, 90, 37, 0, 948, 947, 1, 0, 0, 0, 949, 952, 1, 0, 0, 0, 950, 948, 1, 0, 0, 0, 950, 951, 1, 0, 0, 0, 951, 960, 1, 0, 0, 0, 952, 950, 1, 0, 0, 0, 953, 955, 3, 138, 61, 0, 954, 956, 3, 72, 28, 0, 955, 954, 1, 0, 0, 0, 956, 957, 1, 0, 0, 0, 957, 955, 1, 0, 0, 0, 957, 958, 1, 0, 0, 0, 958, 960, 1, 0, 0, 0, 959, 942, 1, 0, 0, 0, 959, 953, 1, 0, 0, 0, 960, 177, 1, 0, 0, 0, 961, 962, 5, 91, 0, 0, 962, 963, 1, 0, 0, 0, 963, 964, 6, 81, 0, 0, 964, 965, 6, 81, 0, 0, 965, 179, 1, 0, 0, 0, 966, 967, 5, 93, 0, 0, 967, 968, 1, 0, 0, 0, 968, 969, 6, 82, 12, 0, 969, 970, 6, 82, 12, 0, 970, 181, 1, 0, 0, 0, 971, 975, 3, 74, 29, 0, 972, 974, 3, 90, 37, 0, 973, 972, 1, 0, 0, 0, 974, 977, 1, 0, 0, 0, 975, 973, 1, 0, 0, 0, 975, 976, 1, 0, 0, 0, 976, 988, 1, 0, 0, 0, 977, 975, 1, 0, 0, 0, 978, 981, 3, 88, 36, 0, 979, 981, 3, 82, 33, 0, 980, 978, 1, 0, 0, 0, 980, 979, 1, 0, 0, 0, 981, 983, 1, 0, 0, 0, 982, 984, 3, 90, 37, 0, 983, 982, 1, 0, 0, 0, 984, 985, 1, 0, 0, 0, 985, 983, 1, 0, 0, 0, 985, 986, 1, 0, 0, 0, 986, 988, 1, 0, 0, 0, 987, 971, 1, 0, 0, 0, 987, 980, 1, 0, 0, 0, 988, 183, 1, 0, 0, 0, 989, 991, 3, 84, 34, 0, 990, 992, 3, 86, 35, 0, 991, 990, 1, 0, 0, 0, 992, 993, 1, 0, 0, 0, 993, 991, 1, 0, 0, 0, 993, 994, 1, 0, 0, 0, 994, 995, 1, 0, 0, 0, 995, 996, 3, 84, 34, 0, 996, 185, 1, 0, 0, 0, 997, 998, 3, 184, 84, 0, 998, 187, 1, 0, 0, 0, 999, 1000, 3, 64, 24, 0, 1000, 1001, 1, 0, 0, 0, 1001, 1002, 6, 86, 11, 0, 1002, 189, 1, 0, 0, 0, 1003, 1004, 3, 66, 25, 0, 1004, 1005, 1, 0, 0, 0, 1005, 1006, 6, 87, 11, 0, 1006, 191, 1, 0, 0, 0, 1007, 1008, 3, 68, 26, 0, 1008, 1009, 1, 0, 0, 0, 1009, 1010, 6, 88, 11, 0, 1010, 193, 1, 0, 0, 0, 1011, 1012, 3, 178, 81, 0, 1012, 1013, 1, 0, 0, 0, 1013, 1014, 6, 89, 14, 0, 1014, 1015, 6, 89, 15, 0, 1015, 195, 1, 0, 0, 0, 1016, 1017, 3, 70, 27, 0, 1017, 1018, 1, 0, 0, 0, 1018, 1019, 6, 90, 16, 0, 1019, 1020, 6, 90, 12, 0, 1020, 197, 1, 0, 0, 0, 1021, 1022, 3, 68, 26, 0, 1022, 1023, 1, 0, 0, 0, 1023, 1024, 6, 91, 11, 0, 1024, 199, 1, 0, 0, 0, 1025, 1026, 3, 64, 24, 0, 1026, 1027, 1, 0, 0, 0, 1027, 1028, 6, 92, 11, 0, 1028, 201, 1, 0, 0, 0, 1029, 1030, 3, 66, 25, 0, 1030, 1031, 1, 0, 0, 0, 1031, 1032, 6, 93, 11, 0, 1032, 203, 1, 0, 0, 0, 1033, 1034, 3, 70, 27, 0, 1034, 1035, 1, 0, 0, 0, 1035, 1036, 6, 94, 16, 0, 1036, 1037, 6, 94, 12, 0, 1037, 205, 1, 0, 0, 0, 1038, 1039, 3, 178, 81, 0, 1039, 1040, 1, 0, 0, 0, 1040, 1041, 6, 95, 14, 0, 1041, 207, 1, 0, 0, 0, 1042, 1043, 3, 180, 82, 0, 1043, 1044, 1, 0, 0, 0, 1044, 1045, 6, 96, 17, 0, 1045, 209, 1, 0, 0, 0, 1046, 1047, 3, 108, 46, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1049, 6, 97, 18, 0, 1049, 211, 1, 0, 0, 0, 1050, 1051, 3, 110, 47, 0, 1051, 1052, 1, 0, 0, 0, 1052, 1053, 6, 98, 19, 0, 1053, 213, 1, 0, 0, 0, 1054, 1055, 3, 104, 44, 0, 1055, 1056, 1, 0, 0, 0, 1056, 1057, 6, 99, 20, 0, 1057, 215, 1, 0, 0, 0, 1058, 1059, 7, 16, 0, 0, 1059, 1060, 7, 3, 0, 0, 1060, 1061, 7, 5, 0, 0, 1061, 1062, 7, 12, 0, 0, 1062, 1063, 7, 0, 0, 0, 1063, 1064, 7, 12, 0, 0, 1064, 1065, 7, 5, 0, 0, 1065, 1066, 7, 12, 0, 0, 1066, 217, 1, 0, 0, 0, 1067, 1071, 8, 32, 0, 0, 1068, 1069, 5, 47, 0, 0, 1069, 1071, 8, 33, 0, 0, 1070, 1067, 1, 0, 0, 0, 1070, 1068, 1, 0, 0, 0, 1071, 219, 1, 0, 0, 0, 1072, 1074, 3, 218, 101, 0, 1073, 1072, 1, 0, 0, 0, 1074, 1075, 1, 0, 0, 0, 1075, 1073, 1, 0, 0, 0, 1075, 1076, 1, 0, 0, 0, 1076, 221, 1, 0, 0, 0, 1077, 1078, 3, 220, 102, 0, 1078, 1079, 1, 0, 0, 0, 1079, 1080, 6, 103, 21, 0, 1080, 223, 1, 0, 0, 0, 1081, 1082, 3, 92, 38, 0, 1082, 1083, 1, 0, 0, 0, 1083, 1084, 6, 104, 22, 0, 1084, 225, 1, 0, 0, 0, 1085, 1086, 3, 64, 24, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1088, 6, 105, 11, 0, 1088, 227, 1, 0, 0, 0, 1089, 1090, 3, 66, 25, 0, 1090, 1091, 1, 0, 0, 0, 1091, 1092, 6, 106, 11, 0, 1092, 229, 1, 0, 0, 0, 1093, 1094, 3, 68, 26, 0, 1094, 1095, 1, 0, 0, 0, 1095, 1096, 6, 107, 11, 0, 1096, 231, 1, 0, 0, 0, 1097, 1098, 3, 70, 27, 0, 1098, 1099, 1, 0, 0, 0, 1099, 1100, 6, 108, 16, 0, 1100, 1101, 6, 108, 12, 0, 1101, 233, 1, 0, 0, 0, 1102, 1103, 3, 114, 49, 0, 1103, 1104, 1, 0, 0, 0, 1104, 1105, 6, 109, 23, 0, 1105, 235, 1, 0, 0, 0, 1106, 1107, 3, 110, 47, 0, 1107, 1108, 1, 0, 0, 0, 1108, 1109, 6, 110, 19, 0, 1109, 237, 1, 0, 0, 0, 1110, 1111, 4, 111, 6, 0, 1111, 1112, 3, 138, 61, 0, 1112, 1113, 1, 0, 0, 0, 1113, 1114, 6, 111, 24, 0, 1114, 239, 1, 0, 0, 0, 1115, 1116, 4, 112, 7, 0, 1116, 1117, 3, 176, 80, 0, 1117, 1118, 1, 0, 0, 0, 1118, 1119, 6, 112, 25, 0, 1119, 241, 1, 0, 0, 0, 1120, 1125, 3, 74, 29, 0, 1121, 1125, 3, 72, 28, 0, 1122, 1125, 3, 88, 36, 0, 1123, 1125, 3, 164, 74, 0, 1124, 1120, 1, 0, 0, 0, 1124, 1121, 1, 0, 0, 0, 1124, 1122, 1, 0, 0, 0, 1124, 1123, 1, 0, 0, 0, 1125, 243, 1, 0, 0, 0, 1126, 1129, 3, 74, 29, 0, 1127, 1129, 3, 164, 74, 0, 1128, 1126, 1, 0, 0, 0, 1128, 1127, 1, 0, 0, 0, 1129, 1133, 1, 0, 0, 0, 1130, 1132, 3, 242, 113, 0, 1131, 1130, 1, 0, 0, 0, 1132, 1135, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1133, 1134, 1, 0, 0, 0, 1134, 1146, 1, 0, 0, 0, 1135, 1133, 1, 0, 0, 0, 1136, 1139, 3, 88, 36, 0, 1137, 1139, 3, 82, 33, 0, 1138, 1136, 1, 0, 0, 0, 1138, 1137, 1, 0, 0, 0, 1139, 1141, 1, 0, 0, 0, 1140, 1142, 3, 242, 113, 0, 1141, 1140, 1, 0, 0, 0, 1142, 1143, 1, 0, 0, 0, 1143, 1141, 1, 0, 0, 0, 1143, 1144, 1, 0, 0, 0, 1144, 1146, 1, 0, 0, 0, 1145, 1128, 1, 0, 0, 0, 1145, 1138, 1, 0, 0, 0, 1146, 245, 1, 0, 0, 0, 1147, 1150, 3, 244, 114, 0, 1148, 1150, 3, 184, 84, 0, 1149, 1147, 1, 0, 0, 0, 1149, 1148, 1, 0, 0, 0, 1150, 1151, 1, 0, 0, 0, 1151, 1149, 1, 0, 0, 0, 1151, 1152, 1, 0, 0, 0, 1152, 247, 1, 0, 0, 0, 1153, 1154, 3, 64, 24, 0, 1154, 1155, 1, 0, 0, 0, 1155, 1156, 6, 116, 11, 0, 1156, 249, 1, 0, 0, 0, 1157, 1158, 3, 66, 25, 0, 1158, 1159, 1, 0, 0, 0, 1159, 1160, 6, 117, 11, 0, 1160, 251, 1, 0, 0, 0, 1161, 1162, 3, 68, 26, 0, 1162, 1163, 1, 0, 0, 0, 1163, 1164, 6, 118, 11, 0, 1164, 253, 1, 0, 0, 0, 1165, 1166, 3, 70, 27, 0, 1166, 1167, 1, 0, 0, 0, 1167, 1168, 6, 119, 16, 0, 1168, 1169, 6, 119, 12, 0, 1169, 255, 1, 0, 0, 0, 1170, 1171, 3, 104, 44, 0, 1171, 1172, 1, 0, 0, 0, 1172, 1173, 6, 120, 20, 0, 1173, 257, 1, 0, 0, 0, 1174, 1175, 3, 110, 47, 0, 1175, 1176, 1, 0, 0, 0, 1176, 1177, 6, 121, 19, 0, 1177, 259, 1, 0, 0, 0, 1178, 1179, 3, 114, 49, 0, 1179, 1180, 1, 0, 0, 0, 1180, 1181, 6, 122, 23, 0, 1181, 261, 1, 0, 0, 0, 1182, 1183, 4, 123, 8, 0, 1183, 1184, 3, 138, 61, 0, 1184, 1185, 1, 0, 0, 0, 1185, 1186, 6, 123, 24, 0, 1186, 263, 1, 0, 0, 0, 1187, 1188, 4, 124, 9, 0, 1188, 1189, 3, 176, 80, 0, 1189, 1190, 1, 0, 0, 0, 1190, 1191, 6, 124, 25, 0, 1191, 265, 1, 0, 0, 0, 1192, 1193, 7, 12, 0, 0, 1193, 1194, 7, 2, 0, 0, 1194, 267, 1, 0, 0, 0, 1195, 1196, 3, 246, 115, 0, 1196, 1197, 1, 0, 0, 0, 1197, 1198, 6, 126, 26, 0, 1198, 269, 1, 0, 0, 0, 1199, 1200, 3, 64, 24, 0, 1200, 1201, 1, 0, 0, 0, 1201, 1202, 6, 127, 11, 0, 1202, 271, 1, 0, 0, 0, 1203, 1204, 3, 66, 25, 0, 1204, 1205, 1, 0, 0, 0, 1205, 1206, 6, 128, 11, 0, 1206, 273, 1, 0, 0, 0, 1207, 1208, 3, 68, 26, 0, 1208, 1209, 1, 0, 0, 0, 1209, 1210, 6, 129, 11, 0, 1210, 275, 1, 0, 0, 0, 1211, 1212, 3, 70, 27, 0, 1212, 1213, 1, 0, 0, 0, 1213, 1214, 6, 130, 16, 0, 1214, 1215, 6, 130, 12, 0, 1215, 277, 1, 0, 0, 0, 1216, 1217, 3, 178, 81, 0, 1217, 1218, 1, 0, 0, 0, 1218, 1219, 6, 131, 14, 0, 1219, 1220, 6, 131, 27, 0, 1220, 279, 1, 0, 0, 0, 1221, 1222, 7, 7, 0, 0, 1222, 1223, 7, 9, 0, 0, 1223, 1224, 1, 0, 0, 0, 1224, 1225, 6, 132, 28, 0, 1225, 281, 1, 0, 0, 0, 1226, 1227, 7, 19, 0, 0, 1227, 1228, 7, 1, 0, 0, 1228, 1229, 7, 5, 0, 0, 1229, 1230, 7, 10, 0, 0, 1230, 1231, 1, 0, 0, 0, 1231, 1232, 6, 133, 28, 0, 1232, 283, 1, 0, 0, 0, 1233, 1234, 8, 34, 0, 0, 1234, 285, 1, 0, 0, 0, 1235, 1237, 3, 284, 134, 0, 1236, 1235, 1, 0, 0, 0, 1237, 1238, 1, 0, 0, 0, 1238, 1236, 1, 0, 0, 0, 1238, 1239, 1, 0, 0, 0, 1239, 1240, 1, 0, 0, 0, 1240, 1241, 3, 108, 46, 0, 1241, 1243, 1, 0, 0, 0, 1242, 1236, 1, 0, 0, 0, 1242, 1243, 1, 0, 0, 0, 1243, 1245, 1, 0, 0, 0, 1244, 1246, 3, 284, 134, 0, 1245, 1244, 1, 0, 0, 0, 1246, 1247, 1, 0, 0, 0, 1247, 1245, 1, 0, 0, 0, 1247, 1248, 1, 0, 0, 0, 1248, 287, 1, 0, 0, 0, 1249, 1250, 3, 286, 135, 0, 1250, 1251, 1, 0, 0, 0, 1251, 1252, 6, 136, 29, 0, 1252, 289, 1, 0, 0, 0, 1253, 1254, 3, 64, 24, 0, 1254, 1255, 1, 0, 0, 0, 1255, 1256, 6, 137, 11, 0, 1256, 291, 1, 0, 0, 0, 1257, 1258, 3, 66, 25, 0, 1258, 1259, 1, 0, 0, 0, 1259, 1260, 6, 138, 11, 0, 1260, 293, 1, 0, 0, 0, 1261, 1262, 3, 68, 26, 0, 1262, 1263, 1, 0, 0, 0, 1263, 1264, 6, 139, 11, 0, 1264, 295, 1, 0, 0, 0, 1265, 1266, 3, 70, 27, 0, 1266, 1267, 1, 0, 0, 0, 1267, 1268, 6, 140, 16, 0, 1268, 1269, 6, 140, 12, 0, 1269, 1270, 6, 140, 12, 0, 1270, 297, 1, 0, 0, 0, 1271, 1272, 3, 104, 44, 0, 1272, 1273, 1, 0, 0, 0, 1273, 1274, 6, 141, 20, 0, 1274, 299, 1, 0, 0, 0, 1275, 1276, 3, 110, 47, 0, 1276, 1277, 1, 0, 0, 0, 1277, 1278, 6, 142, 19, 0, 1278, 301, 1, 0, 0, 0, 1279, 1280, 3, 114, 49, 0, 1280, 1281, 1, 0, 0, 0, 1281, 1282, 6, 143, 23, 0, 1282, 303, 1, 0, 0, 0, 1283, 1284, 3, 282, 133, 0, 1284, 1285, 1, 0, 0, 0, 1285, 1286, 6, 144, 30, 0, 1286, 305, 1, 0, 0, 0, 1287, 1288, 3, 246, 115, 0, 1288, 1289, 1, 0, 0, 0, 1289, 1290, 6, 145, 26, 0, 1290, 307, 1, 0, 0, 0, 1291, 1292, 3, 186, 85, 0, 1292, 1293, 1, 0, 0, 0, 1293, 1294, 6, 146, 31, 0, 1294, 309, 1, 0, 0, 0, 1295, 1296, 4, 147, 10, 0, 1296, 1297, 3, 138, 61, 0, 1297, 1298, 1, 0, 0, 0, 1298, 1299, 6, 147, 24, 0, 1299, 311, 1, 0, 0, 0, 1300, 1301, 4, 148, 11, 0, 1301, 1302, 3, 176, 80, 0, 1302, 1303, 1, 0, 0, 0, 1303, 1304, 6, 148, 25, 0, 1304, 313, 1, 0, 0, 0, 1305, 1306, 3, 64, 24, 0, 1306, 1307, 1, 0, 0, 0, 1307, 1308, 6, 149, 11, 0, 1308, 315, 1, 0, 0, 0, 1309, 1310, 3, 66, 25, 0, 1310, 1311, 1, 0, 0, 0, 1311, 1312, 6, 150, 11, 0, 1312, 317, 1, 0, 0, 0, 1313, 1314, 3, 68, 26, 0, 1314, 1315, 1, 0, 0, 0, 1315, 1316, 6, 151, 11, 0, 1316, 319, 1, 0, 0, 0, 1317, 1318, 3, 70, 27, 0, 1318, 1319, 1, 0, 0, 0, 1319, 1320, 6, 152, 16, 0, 1320, 1321, 6, 152, 12, 0, 1321, 321, 1, 0, 0, 0, 1322, 1323, 3, 114, 49, 0, 1323, 1324, 1, 0, 0, 0, 1324, 1325, 6, 153, 23, 0, 1325, 323, 1, 0, 0, 0, 1326, 1327, 4, 154, 12, 0, 1327, 1328, 3, 138, 61, 0, 1328, 1329, 1, 0, 0, 0, 1329, 1330, 6, 154, 24, 0, 1330, 325, 1, 0, 0, 0, 1331, 1332, 4, 155, 13, 0, 1332, 1333, 3, 176, 80, 0, 1333, 1334, 1, 0, 0, 0, 1334, 1335, 6, 155, 25, 0, 1335, 327, 1, 0, 0, 0, 1336, 1337, 3, 186, 85, 0, 1337, 1338, 1, 0, 0, 0, 1338, 1339, 6, 156, 31, 0, 1339, 329, 1, 0, 0, 0, 1340, 1341, 3, 182, 83, 0, 1341, 1342, 1, 0, 0, 0, 1342, 1343, 6, 157, 32, 0, 1343, 331, 1, 0, 0, 0, 1344, 1345, 3, 64, 24, 0, 1345, 1346, 1, 0, 0, 0, 1346, 1347, 6, 158, 11, 0, 1347, 333, 1, 0, 0, 0, 1348, 1349, 3, 66, 25, 0, 1349, 1350, 1, 0, 0, 0, 1350, 1351, 6, 159, 11, 0, 1351, 335, 1, 0, 0, 0, 1352, 1353, 3, 68, 26, 0, 1353, 1354, 1, 0, 0, 0, 1354, 1355, 6, 160, 11, 0, 1355, 337, 1, 0, 0, 0, 1356, 1357, 3, 70, 27, 0, 1357, 1358, 1, 0, 0, 0, 1358, 1359, 6, 161, 16, 0, 1359, 1360, 6, 161, 12, 0, 1360, 339, 1, 0, 0, 0, 1361, 1362, 7, 1, 0, 0, 1362, 1363, 7, 9, 0, 0, 1363, 1364, 7, 15, 0, 0, 1364, 1365, 7, 7, 0, 0, 1365, 341, 1, 0, 0, 0, 1366, 1367, 3, 64, 24, 0, 1367, 1368, 1, 0, 0, 0, 1368, 1369, 6, 163, 11, 0, 1369, 343, 1, 0, 0, 0, 1370, 1371, 3, 66, 25, 0, 1371, 1372, 1, 0, 0, 0, 1372, 1373, 6, 164, 11, 0, 1373, 345, 1, 0, 0, 0, 1374, 1375, 3, 68, 26, 0, 1375, 1376, 1, 0, 0, 0, 1376, 1377, 6, 165, 11, 0, 1377, 347, 1, 0, 0, 0, 1378, 1379, 3, 180, 82, 0, 1379, 1380, 1, 0, 0, 0, 1380, 1381, 6, 166, 17, 0, 1381, 1382, 6, 166, 12, 0, 1382, 349, 1, 0, 0, 0, 1383, 1384, 3, 108, 46, 0, 1384, 1385, 1, 0, 0, 0, 1385, 1386, 6, 167, 18, 0, 1386, 351, 1, 0, 0, 0, 1387, 1393, 3, 82, 33, 0, 1388, 1393, 3, 72, 28, 0, 1389, 1393, 3, 114, 49, 0, 1390, 1393, 3, 74, 29, 0, 1391, 1393, 3, 88, 36, 0, 1392, 1387, 1, 0, 0, 0, 1392, 1388, 1, 0, 0, 0, 1392, 1389, 1, 0, 0, 0, 1392, 1390, 1, 0, 0, 0, 1392, 1391, 1, 0, 0, 0, 1393, 1394, 1, 0, 0, 0, 1394, 1392, 1, 0, 0, 0, 1394, 1395, 1, 0, 0, 0, 1395, 353, 1, 0, 0, 0, 1396, 1397, 3, 64, 24, 0, 1397, 1398, 1, 0, 0, 0, 1398, 1399, 6, 169, 11, 0, 1399, 355, 1, 0, 0, 0, 1400, 1401, 3, 66, 25, 0, 1401, 1402, 1, 0, 0, 0, 1402, 1403, 6, 170, 11, 0, 1403, 357, 1, 0, 0, 0, 1404, 1405, 3, 68, 26, 0, 1405, 1406, 1, 0, 0, 0, 1406, 1407, 6, 171, 11, 0, 1407, 359, 1, 0, 0, 0, 1408, 1409, 3, 70, 27, 0, 1409, 1410, 1, 0, 0, 0, 1410, 1411, 6, 172, 16, 0, 1411, 1412, 6, 172, 12, 0, 1412, 361, 1, 0, 0, 0, 1413, 1414, 3, 108, 46, 0, 1414, 1415, 1, 0, 0, 0, 1415, 1416, 6, 173, 18, 0, 1416, 363, 1, 0, 0, 0, 1417, 1418, 3, 110, 47, 0, 1418, 1419, 1, 0, 0, 0, 1419, 1420, 6, 174, 19, 0, 1420, 365, 1, 0, 0, 0, 1421, 1422, 3, 114, 49, 0, 1422, 1423, 1, 0, 0, 0, 1423, 1424, 6, 175, 23, 0, 1424, 367, 1, 0, 0, 0, 1425, 1426, 3, 280, 132, 0, 1426, 1427, 1, 0, 0, 0, 1427, 1428, 6, 176, 33, 0, 1428, 1429, 6, 176, 34, 0, 1429, 369, 1, 0, 0, 0, 1430, 1431, 3, 220, 102, 0, 1431, 1432, 1, 0, 0, 0, 1432, 1433, 6, 177, 21, 0, 1433, 371, 1, 0, 0, 0, 1434, 1435, 3, 92, 38, 0, 1435, 1436, 1, 0, 0, 0, 1436, 1437, 6, 178, 22, 0, 1437, 373, 1, 0, 0, 0, 1438, 1439, 3, 64, 24, 0, 1439, 1440, 1, 0, 0, 0, 1440, 1441, 6, 179, 11, 0, 1441, 375, 1, 0, 0, 0, 1442, 1443, 3, 66, 25, 0, 1443, 1444, 1, 0, 0, 0, 1444, 1445, 6, 180, 11, 0, 1445, 377, 1, 0, 0, 0, 1446, 1447, 3, 68, 26, 0, 1447, 1448, 1, 0, 0, 0, 1448, 1449, 6, 181, 11, 0, 1449, 379, 1, 0, 0, 0, 1450, 1451, 3, 70, 27, 0, 1451, 1452, 1, 0, 0, 0, 1452, 1453, 6, 182, 16, 0, 1453, 1454, 6, 182, 12, 0, 1454, 1455, 6, 182, 12, 0, 1455, 381, 1, 0, 0, 0, 1456, 1457, 3, 110, 47, 0, 1457, 1458, 1, 0, 0, 0, 1458, 1459, 6, 183, 19, 0, 1459, 383, 1, 0, 0, 0, 1460, 1461, 3, 114, 49, 0, 1461, 1462, 1, 0, 0, 0, 1462, 1463, 6, 184, 23, 0, 1463, 385, 1, 0, 0, 0, 1464, 1465, 3, 246, 115, 0, 1465, 1466, 1, 0, 0, 0, 1466, 1467, 6, 185, 26, 0, 1467, 387, 1, 0, 0, 0, 1468, 1469, 3, 64, 24, 0, 1469, 1470, 1, 0, 0, 0, 1470, 1471, 6, 186, 11, 0, 1471, 389, 1, 0, 0, 0, 1472, 1473, 3, 66, 25, 0, 1473, 1474, 1, 0, 0, 0, 1474, 1475, 6, 187, 11, 0, 1475, 391, 1, 0, 0, 0, 1476, 1477, 3, 68, 26, 0, 1477, 1478, 1, 0, 0, 0, 1478, 1479, 6, 188, 11, 0, 1479, 393, 1, 0, 0, 0, 1480, 1481, 3, 70, 27, 0, 1481, 1482, 1, 0, 0, 0, 1482, 1483, 6, 189, 16, 0, 1483, 1484, 6, 189, 12, 0, 1484, 395, 1, 0, 0, 0, 1485, 1486, 7, 35, 0, 0, 1486, 1487, 7, 7, 0, 0, 1487, 1488, 7, 1, 0, 0, 1488, 1489, 7, 9, 0, 0, 1489, 397, 1, 0, 0, 0, 1490, 1491, 3, 266, 125, 0, 1491, 1492, 1, 0, 0, 0, 1492, 1493, 6, 191, 35, 0, 1493, 399, 1, 0, 0, 0, 1494, 1495, 3, 280, 132, 0, 1495, 1496, 1, 0, 0, 0, 1496, 1497, 6, 192, 33, 0, 1497, 1498, 6, 192, 12, 0, 1498, 1499, 6, 192, 0, 0, 1499, 401, 1, 0, 0, 0, 1500, 1501, 7, 20, 0, 0, 1501, 1502, 7, 2, 0, 0, 1502, 1503, 7, 1, 0, 0, 1503, 1504, 7, 9, 0, 0, 1504, 1505, 7, 17, 0, 0, 1505, 1506, 1, 0, 0, 0, 1506, 1507, 6, 193, 12, 0, 1507, 1508, 6, 193, 0, 0, 1508, 403, 1, 0, 0, 0, 1509, 1510, 3, 220, 102, 0, 1510, 1511, 1, 0, 0, 0, 1511, 1512, 6, 194, 21, 0, 1512, 405, 1, 0, 0, 0, 1513, 1514, 3, 92, 38, 0, 1514, 1515, 1, 0, 0, 0, 1515, 1516, 6, 195, 22, 0, 1516, 407, 1, 0, 0, 0, 1517, 1518, 3, 108, 46, 0, 1518, 1519, 1, 0, 0, 0, 1519, 1520, 6, 196, 18, 0, 1520, 409, 1, 0, 0, 0, 1521, 1522, 3, 182, 83, 0, 1522, 1523, 1, 0, 0, 0, 1523, 1524, 6, 197, 32, 0, 1524, 411, 1, 0, 0, 0, 1525, 1526, 3, 186, 85, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 6, 198, 31, 0, 1528, 413, 1, 0, 0, 0, 1529, 1530, 3, 64, 24, 0, 1530, 1531, 1, 0, 0, 0, 1531, 1532, 6, 199, 11, 0, 1532, 415, 1, 0, 0, 0, 1533, 1534, 3, 66, 25, 0, 1534, 1535, 1, 0, 0, 0, 1535, 1536, 6, 200, 11, 0, 1536, 417, 1, 0, 0, 0, 1537, 1538, 3, 68, 26, 0, 1538, 1539, 1, 0, 0, 0, 1539, 1540, 6, 201, 11, 0, 1540, 419, 1, 0, 0, 0, 1541, 1542, 3, 70, 27, 0, 1542, 1543, 1, 0, 0, 0, 1543, 1544, 6, 202, 16, 0, 1544, 1545, 6, 202, 12, 0, 1545, 421, 1, 0, 0, 0, 1546, 1547, 3, 220, 102, 0, 1547, 1548, 1, 0, 0, 0, 1548, 1549, 6, 203, 21, 0, 1549, 1550, 6, 203, 12, 0, 1550, 1551, 6, 203, 36, 0, 1551, 423, 1, 0, 0, 0, 1552, 1553, 3, 92, 38, 0, 1553, 1554, 1, 0, 0, 0, 1554, 1555, 6, 204, 22, 0, 1555, 1556, 6, 204, 12, 0, 1556, 1557, 6, 204, 36, 0, 1557, 425, 1, 0, 0, 0, 1558, 1559, 3, 64, 24, 0, 1559, 1560, 1, 0, 0, 0, 1560, 1561, 6, 205, 11, 0, 1561, 427, 1, 0, 0, 0, 1562, 1563, 3, 66, 25, 0, 1563, 1564, 1, 0, 0, 0, 1564, 1565, 6, 206, 11, 0, 1565, 429, 1, 0, 0, 0, 1566, 1567, 3, 68, 26, 0, 1567, 1568, 1, 0, 0, 0, 1568, 1569, 6, 207, 11, 0, 1569, 431, 1, 0, 0, 0, 1570, 1571, 3, 108, 46, 0, 1571, 1572, 1, 0, 0, 0, 1572, 1573, 6, 208, 18, 0, 1573, 1574, 6, 208, 12, 0, 1574, 1575, 6, 208, 10, 0, 1575, 433, 1, 0, 0, 0, 1576, 1577, 3, 110, 47, 0, 1577, 1578, 1, 0, 0, 0, 1578, 1579, 6, 209, 19, 0, 1579, 1580, 6, 209, 12, 0, 1580, 1581, 6, 209, 10, 0, 1581, 435, 1, 0, 0, 0, 1582, 1583, 3, 64, 24, 0, 1583, 1584, 1, 0, 0, 0, 1584, 1585, 6, 210, 11, 0, 1585, 437, 1, 0, 0, 0, 1586, 1587, 3, 66, 25, 0, 1587, 1588, 1, 0, 0, 0, 1588, 1589, 6, 211, 11, 0, 1589, 439, 1, 0, 0, 0, 1590, 1591, 3, 68, 26, 0, 1591, 1592, 1, 0, 0, 0, 1592, 1593, 6, 212, 11, 0, 1593, 441, 1, 0, 0, 0, 1594, 1595, 3, 186, 85, 0, 1595, 1596, 1, 0, 0, 0, 1596, 1597, 6, 213, 12, 0, 1597, 1598, 6, 213, 0, 0, 1598, 1599, 6, 213, 31, 0, 1599, 443, 1, 0, 0, 0, 1600, 1601, 3, 182, 83, 0, 1601, 1602, 1, 0, 0, 0, 1602, 1603, 6, 214, 12, 0, 1603, 1604, 6, 214, 0, 0, 1604, 1605, 6, 214, 32, 0, 1605, 445, 1, 0, 0, 0, 1606, 1607, 3, 98, 41, 0, 1607, 1608, 1, 0, 0, 0, 1608, 1609, 6, 215, 12, 0, 1609, 1610, 6, 215, 0, 0, 1610, 1611, 6, 215, 37, 0, 1611, 447, 1, 0, 0, 0, 1612, 1613, 3, 70, 27, 0, 1613, 1614, 1, 0, 0, 0, 1614, 1615, 6, 216, 16, 0, 1615, 1616, 6, 216, 12, 0, 1616, 449, 1, 0, 0, 0, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 654, 664, 668, 671, 680, 682, 693, 712, 717, 726, 733, 738, 740, 751, 759, 762, 764, 769, 774, 780, 787, 792, 798, 801, 809, 813, 945, 950, 957, 959, 975, 980, 985, 987, 993, 1070, 1075, 1124, 1128, 1133, 1138, 1143, 1145, 1149, 1151, 1238, 1242, 1247, 1392, 1394, 38, 5, 1, 0, 5, 4, 0, 5, 6, 0, 5, 2, 0, 5, 3, 0, 5, 8, 0, 5, 5, 0, 5, 9, 0, 5, 13, 0, 5, 11, 0, 5, 14, 0, 0, 1, 0, 4, 0, 0, 7, 16, 0, 7, 71, 0, 5, 0, 0, 7, 28, 0, 7, 72, 0, 7, 37, 0, 7, 38, 0, 7, 35, 0, 7, 82, 0, 7, 29, 0, 7, 40, 0, 7, 52, 0, 7, 70, 0, 7, 86, 0, 5, 10, 0, 5, 7, 0, 7, 96, 0, 7, 95, 0, 7, 74, 0, 7, 73, 0, 7, 94, 0, 5, 12, 0, 7, 90, 0, 5, 15, 0, 7, 32, 0] \ No newline at end of file diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.tokens b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.tokens index 366b455f16402..02af324872fc0 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.tokens +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.tokens @@ -14,110 +14,110 @@ SHOW=13 SORT=14 STATS=15 WHERE=16 -DEV_INLINESTATS=17 -DEV_LOOKUP=18 -DEV_METRICS=19 -DEV_JOIN=20 +JOIN_LOOKUP=17 +DEV_INLINESTATS=18 +DEV_LOOKUP=19 +DEV_METRICS=20 DEV_JOIN_FULL=21 DEV_JOIN_LEFT=22 DEV_JOIN_RIGHT=23 -DEV_JOIN_LOOKUP=24 -UNKNOWN_CMD=25 -LINE_COMMENT=26 -MULTILINE_COMMENT=27 -WS=28 -PIPE=29 -QUOTED_STRING=30 -INTEGER_LITERAL=31 -DECIMAL_LITERAL=32 -BY=33 -AND=34 -ASC=35 -ASSIGN=36 -CAST_OP=37 -COLON=38 -COMMA=39 -DESC=40 -DOT=41 -FALSE=42 -FIRST=43 -IN=44 -IS=45 -LAST=46 -LIKE=47 -LP=48 -NOT=49 -NULL=50 -NULLS=51 -OR=52 -PARAM=53 -RLIKE=54 -RP=55 -TRUE=56 -EQ=57 -CIEQ=58 -NEQ=59 -LT=60 -LTE=61 -GT=62 -GTE=63 -PLUS=64 -MINUS=65 -ASTERISK=66 -SLASH=67 -PERCENT=68 -LEFT_BRACES=69 -RIGHT_BRACES=70 -NAMED_OR_POSITIONAL_PARAM=71 -OPENING_BRACKET=72 -CLOSING_BRACKET=73 -UNQUOTED_IDENTIFIER=74 -QUOTED_IDENTIFIER=75 -EXPR_LINE_COMMENT=76 -EXPR_MULTILINE_COMMENT=77 -EXPR_WS=78 -EXPLAIN_WS=79 -EXPLAIN_LINE_COMMENT=80 -EXPLAIN_MULTILINE_COMMENT=81 -METADATA=82 -UNQUOTED_SOURCE=83 -FROM_LINE_COMMENT=84 -FROM_MULTILINE_COMMENT=85 -FROM_WS=86 -ID_PATTERN=87 -PROJECT_LINE_COMMENT=88 -PROJECT_MULTILINE_COMMENT=89 -PROJECT_WS=90 -AS=91 -RENAME_LINE_COMMENT=92 -RENAME_MULTILINE_COMMENT=93 -RENAME_WS=94 -ON=95 -WITH=96 -ENRICH_POLICY_NAME=97 -ENRICH_LINE_COMMENT=98 -ENRICH_MULTILINE_COMMENT=99 -ENRICH_WS=100 -ENRICH_FIELD_LINE_COMMENT=101 -ENRICH_FIELD_MULTILINE_COMMENT=102 -ENRICH_FIELD_WS=103 -MVEXPAND_LINE_COMMENT=104 -MVEXPAND_MULTILINE_COMMENT=105 -MVEXPAND_WS=106 -INFO=107 -SHOW_LINE_COMMENT=108 -SHOW_MULTILINE_COMMENT=109 -SHOW_WS=110 -SETTING=111 -SETTING_LINE_COMMENT=112 -SETTTING_MULTILINE_COMMENT=113 -SETTING_WS=114 -LOOKUP_LINE_COMMENT=115 -LOOKUP_MULTILINE_COMMENT=116 -LOOKUP_WS=117 -LOOKUP_FIELD_LINE_COMMENT=118 -LOOKUP_FIELD_MULTILINE_COMMENT=119 -LOOKUP_FIELD_WS=120 +UNKNOWN_CMD=24 +LINE_COMMENT=25 +MULTILINE_COMMENT=26 +WS=27 +PIPE=28 +QUOTED_STRING=29 +INTEGER_LITERAL=30 +DECIMAL_LITERAL=31 +BY=32 +AND=33 +ASC=34 +ASSIGN=35 +CAST_OP=36 +COLON=37 +COMMA=38 +DESC=39 +DOT=40 +FALSE=41 +FIRST=42 +IN=43 +IS=44 +LAST=45 +LIKE=46 +LP=47 +NOT=48 +NULL=49 +NULLS=50 +OR=51 +PARAM=52 +RLIKE=53 +RP=54 +TRUE=55 +EQ=56 +CIEQ=57 +NEQ=58 +LT=59 +LTE=60 +GT=61 +GTE=62 +PLUS=63 +MINUS=64 +ASTERISK=65 +SLASH=66 +PERCENT=67 +LEFT_BRACES=68 +RIGHT_BRACES=69 +NAMED_OR_POSITIONAL_PARAM=70 +OPENING_BRACKET=71 +CLOSING_BRACKET=72 +UNQUOTED_IDENTIFIER=73 +QUOTED_IDENTIFIER=74 +EXPR_LINE_COMMENT=75 +EXPR_MULTILINE_COMMENT=76 +EXPR_WS=77 +EXPLAIN_WS=78 +EXPLAIN_LINE_COMMENT=79 +EXPLAIN_MULTILINE_COMMENT=80 +METADATA=81 +UNQUOTED_SOURCE=82 +FROM_LINE_COMMENT=83 +FROM_MULTILINE_COMMENT=84 +FROM_WS=85 +ID_PATTERN=86 +PROJECT_LINE_COMMENT=87 +PROJECT_MULTILINE_COMMENT=88 +PROJECT_WS=89 +AS=90 +RENAME_LINE_COMMENT=91 +RENAME_MULTILINE_COMMENT=92 +RENAME_WS=93 +ON=94 +WITH=95 +ENRICH_POLICY_NAME=96 +ENRICH_LINE_COMMENT=97 +ENRICH_MULTILINE_COMMENT=98 +ENRICH_WS=99 +ENRICH_FIELD_LINE_COMMENT=100 +ENRICH_FIELD_MULTILINE_COMMENT=101 +ENRICH_FIELD_WS=102 +MVEXPAND_LINE_COMMENT=103 +MVEXPAND_MULTILINE_COMMENT=104 +MVEXPAND_WS=105 +INFO=106 +SHOW_LINE_COMMENT=107 +SHOW_MULTILINE_COMMENT=108 +SHOW_WS=109 +SETTING=110 +SETTING_LINE_COMMENT=111 +SETTTING_MULTILINE_COMMENT=112 +SETTING_WS=113 +LOOKUP_LINE_COMMENT=114 +LOOKUP_MULTILINE_COMMENT=115 +LOOKUP_WS=116 +LOOKUP_FIELD_LINE_COMMENT=117 +LOOKUP_FIELD_MULTILINE_COMMENT=118 +LOOKUP_FIELD_WS=119 +JOIN=120 USING=121 JOIN_LINE_COMMENT=122 JOIN_MULTILINE_COMMENT=123 @@ -144,47 +144,51 @@ CLOSING_METRICS_WS=130 'sort'=14 'stats'=15 'where'=16 -'|'=29 -'by'=33 -'and'=34 -'asc'=35 -'='=36 -'::'=37 -':'=38 -','=39 -'desc'=40 -'.'=41 -'false'=42 -'first'=43 -'in'=44 -'is'=45 -'last'=46 -'like'=47 -'('=48 -'not'=49 -'null'=50 -'nulls'=51 -'or'=52 -'?'=53 -'rlike'=54 -')'=55 -'true'=56 -'=='=57 -'=~'=58 -'!='=59 -'<'=60 -'<='=61 -'>'=62 -'>='=63 -'+'=64 -'-'=65 -'*'=66 -'/'=67 -'%'=68 -']'=73 -'metadata'=82 -'as'=91 -'on'=95 -'with'=96 -'info'=107 +'lookup'=17 +'|'=28 +'by'=32 +'and'=33 +'asc'=34 +'='=35 +'::'=36 +':'=37 +','=38 +'desc'=39 +'.'=40 +'false'=41 +'first'=42 +'in'=43 +'is'=44 +'last'=45 +'like'=46 +'('=47 +'not'=48 +'null'=49 +'nulls'=50 +'or'=51 +'?'=52 +'rlike'=53 +')'=54 +'true'=55 +'=='=56 +'=~'=57 +'!='=58 +'<'=59 +'<='=60 +'>'=61 +'>='=62 +'+'=63 +'-'=64 +'*'=65 +'/'=66 +'%'=67 +'{'=68 +'}'=69 +']'=72 +'metadata'=81 +'as'=90 +'on'=94 +'with'=95 +'info'=106 +'join'=120 'USING'=121 diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.ts b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.ts index a79ec6da36125..8056c9a3c5f44 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_lexer.ts @@ -39,110 +39,110 @@ export default class esql_lexer extends lexer_config { public static readonly SORT = 14; public static readonly STATS = 15; public static readonly WHERE = 16; - public static readonly DEV_INLINESTATS = 17; - public static readonly DEV_LOOKUP = 18; - public static readonly DEV_METRICS = 19; - public static readonly DEV_JOIN = 20; + public static readonly JOIN_LOOKUP = 17; + public static readonly DEV_INLINESTATS = 18; + public static readonly DEV_LOOKUP = 19; + public static readonly DEV_METRICS = 20; public static readonly DEV_JOIN_FULL = 21; public static readonly DEV_JOIN_LEFT = 22; public static readonly DEV_JOIN_RIGHT = 23; - public static readonly DEV_JOIN_LOOKUP = 24; - public static readonly UNKNOWN_CMD = 25; - public static readonly LINE_COMMENT = 26; - public static readonly MULTILINE_COMMENT = 27; - public static readonly WS = 28; - public static readonly PIPE = 29; - public static readonly QUOTED_STRING = 30; - public static readonly INTEGER_LITERAL = 31; - public static readonly DECIMAL_LITERAL = 32; - public static readonly BY = 33; - public static readonly AND = 34; - public static readonly ASC = 35; - public static readonly ASSIGN = 36; - public static readonly CAST_OP = 37; - public static readonly COLON = 38; - public static readonly COMMA = 39; - public static readonly DESC = 40; - public static readonly DOT = 41; - public static readonly FALSE = 42; - public static readonly FIRST = 43; - public static readonly IN = 44; - public static readonly IS = 45; - public static readonly LAST = 46; - public static readonly LIKE = 47; - public static readonly LP = 48; - public static readonly NOT = 49; - public static readonly NULL = 50; - public static readonly NULLS = 51; - public static readonly OR = 52; - public static readonly PARAM = 53; - public static readonly RLIKE = 54; - public static readonly RP = 55; - public static readonly TRUE = 56; - public static readonly EQ = 57; - public static readonly CIEQ = 58; - public static readonly NEQ = 59; - public static readonly LT = 60; - public static readonly LTE = 61; - public static readonly GT = 62; - public static readonly GTE = 63; - public static readonly PLUS = 64; - public static readonly MINUS = 65; - public static readonly ASTERISK = 66; - public static readonly SLASH = 67; - public static readonly PERCENT = 68; - public static readonly LEFT_BRACES = 69; - public static readonly RIGHT_BRACES = 70; - public static readonly NAMED_OR_POSITIONAL_PARAM = 71; - public static readonly OPENING_BRACKET = 72; - public static readonly CLOSING_BRACKET = 73; - public static readonly UNQUOTED_IDENTIFIER = 74; - public static readonly QUOTED_IDENTIFIER = 75; - public static readonly EXPR_LINE_COMMENT = 76; - public static readonly EXPR_MULTILINE_COMMENT = 77; - public static readonly EXPR_WS = 78; - public static readonly EXPLAIN_WS = 79; - public static readonly EXPLAIN_LINE_COMMENT = 80; - public static readonly EXPLAIN_MULTILINE_COMMENT = 81; - public static readonly METADATA = 82; - public static readonly UNQUOTED_SOURCE = 83; - public static readonly FROM_LINE_COMMENT = 84; - public static readonly FROM_MULTILINE_COMMENT = 85; - public static readonly FROM_WS = 86; - public static readonly ID_PATTERN = 87; - public static readonly PROJECT_LINE_COMMENT = 88; - public static readonly PROJECT_MULTILINE_COMMENT = 89; - public static readonly PROJECT_WS = 90; - public static readonly AS = 91; - public static readonly RENAME_LINE_COMMENT = 92; - public static readonly RENAME_MULTILINE_COMMENT = 93; - public static readonly RENAME_WS = 94; - public static readonly ON = 95; - public static readonly WITH = 96; - public static readonly ENRICH_POLICY_NAME = 97; - public static readonly ENRICH_LINE_COMMENT = 98; - public static readonly ENRICH_MULTILINE_COMMENT = 99; - public static readonly ENRICH_WS = 100; - public static readonly ENRICH_FIELD_LINE_COMMENT = 101; - public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 102; - public static readonly ENRICH_FIELD_WS = 103; - public static readonly MVEXPAND_LINE_COMMENT = 104; - public static readonly MVEXPAND_MULTILINE_COMMENT = 105; - public static readonly MVEXPAND_WS = 106; - public static readonly INFO = 107; - public static readonly SHOW_LINE_COMMENT = 108; - public static readonly SHOW_MULTILINE_COMMENT = 109; - public static readonly SHOW_WS = 110; - public static readonly SETTING = 111; - public static readonly SETTING_LINE_COMMENT = 112; - public static readonly SETTTING_MULTILINE_COMMENT = 113; - public static readonly SETTING_WS = 114; - public static readonly LOOKUP_LINE_COMMENT = 115; - public static readonly LOOKUP_MULTILINE_COMMENT = 116; - public static readonly LOOKUP_WS = 117; - public static readonly LOOKUP_FIELD_LINE_COMMENT = 118; - public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 119; - public static readonly LOOKUP_FIELD_WS = 120; + public static readonly UNKNOWN_CMD = 24; + public static readonly LINE_COMMENT = 25; + public static readonly MULTILINE_COMMENT = 26; + public static readonly WS = 27; + public static readonly PIPE = 28; + public static readonly QUOTED_STRING = 29; + public static readonly INTEGER_LITERAL = 30; + public static readonly DECIMAL_LITERAL = 31; + public static readonly BY = 32; + public static readonly AND = 33; + public static readonly ASC = 34; + public static readonly ASSIGN = 35; + public static readonly CAST_OP = 36; + public static readonly COLON = 37; + public static readonly COMMA = 38; + public static readonly DESC = 39; + public static readonly DOT = 40; + public static readonly FALSE = 41; + public static readonly FIRST = 42; + public static readonly IN = 43; + public static readonly IS = 44; + public static readonly LAST = 45; + public static readonly LIKE = 46; + public static readonly LP = 47; + public static readonly NOT = 48; + public static readonly NULL = 49; + public static readonly NULLS = 50; + public static readonly OR = 51; + public static readonly PARAM = 52; + public static readonly RLIKE = 53; + public static readonly RP = 54; + public static readonly TRUE = 55; + public static readonly EQ = 56; + public static readonly CIEQ = 57; + public static readonly NEQ = 58; + public static readonly LT = 59; + public static readonly LTE = 60; + public static readonly GT = 61; + public static readonly GTE = 62; + public static readonly PLUS = 63; + public static readonly MINUS = 64; + public static readonly ASTERISK = 65; + public static readonly SLASH = 66; + public static readonly PERCENT = 67; + public static readonly LEFT_BRACES = 68; + public static readonly RIGHT_BRACES = 69; + public static readonly NAMED_OR_POSITIONAL_PARAM = 70; + public static readonly OPENING_BRACKET = 71; + public static readonly CLOSING_BRACKET = 72; + public static readonly UNQUOTED_IDENTIFIER = 73; + public static readonly QUOTED_IDENTIFIER = 74; + public static readonly EXPR_LINE_COMMENT = 75; + public static readonly EXPR_MULTILINE_COMMENT = 76; + public static readonly EXPR_WS = 77; + public static readonly EXPLAIN_WS = 78; + public static readonly EXPLAIN_LINE_COMMENT = 79; + public static readonly EXPLAIN_MULTILINE_COMMENT = 80; + public static readonly METADATA = 81; + public static readonly UNQUOTED_SOURCE = 82; + public static readonly FROM_LINE_COMMENT = 83; + public static readonly FROM_MULTILINE_COMMENT = 84; + public static readonly FROM_WS = 85; + public static readonly ID_PATTERN = 86; + public static readonly PROJECT_LINE_COMMENT = 87; + public static readonly PROJECT_MULTILINE_COMMENT = 88; + public static readonly PROJECT_WS = 89; + public static readonly AS = 90; + public static readonly RENAME_LINE_COMMENT = 91; + public static readonly RENAME_MULTILINE_COMMENT = 92; + public static readonly RENAME_WS = 93; + public static readonly ON = 94; + public static readonly WITH = 95; + public static readonly ENRICH_POLICY_NAME = 96; + public static readonly ENRICH_LINE_COMMENT = 97; + public static readonly ENRICH_MULTILINE_COMMENT = 98; + public static readonly ENRICH_WS = 99; + public static readonly ENRICH_FIELD_LINE_COMMENT = 100; + public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 101; + public static readonly ENRICH_FIELD_WS = 102; + public static readonly MVEXPAND_LINE_COMMENT = 103; + public static readonly MVEXPAND_MULTILINE_COMMENT = 104; + public static readonly MVEXPAND_WS = 105; + public static readonly INFO = 106; + public static readonly SHOW_LINE_COMMENT = 107; + public static readonly SHOW_MULTILINE_COMMENT = 108; + public static readonly SHOW_WS = 109; + public static readonly SETTING = 110; + public static readonly SETTING_LINE_COMMENT = 111; + public static readonly SETTTING_MULTILINE_COMMENT = 112; + public static readonly SETTING_WS = 113; + public static readonly LOOKUP_LINE_COMMENT = 114; + public static readonly LOOKUP_MULTILINE_COMMENT = 115; + public static readonly LOOKUP_WS = 116; + public static readonly LOOKUP_FIELD_LINE_COMMENT = 117; + public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 118; + public static readonly LOOKUP_FIELD_WS = 119; + public static readonly JOIN = 120; public static readonly USING = 121; public static readonly JOIN_LINE_COMMENT = 122; public static readonly JOIN_MULTILINE_COMMENT = 123; @@ -180,40 +180,39 @@ export default class esql_lexer extends lexer_config { "'rename'", "'row'", "'show'", "'sort'", "'stats'", - "'where'", null, + "'where'", "'lookup'", null, null, null, null, null, null, null, null, null, null, - null, "'|'", + "'|'", null, null, null, - null, "'by'", - "'and'", "'asc'", - "'='", "'::'", - "':'", "','", - "'desc'", "'.'", - "'false'", "'first'", - "'in'", "'is'", - "'last'", "'like'", - "'('", "'not'", - "'null'", "'nulls'", - "'or'", "'?'", - "'rlike'", "')'", - "'true'", "'=='", - "'=~'", "'!='", - "'<'", "'<='", - "'>'", "'>='", - "'+'", "'-'", - "'*'", "'/'", - "'%'", null, + "'by'", "'and'", + "'asc'", "'='", + "'::'", "':'", + "','", "'desc'", + "'.'", "'false'", + "'first'", "'in'", + "'is'", "'last'", + "'like'", "'('", + "'not'", "'null'", + "'nulls'", "'or'", + "'?'", "'rlike'", + "')'", "'true'", + "'=='", "'=~'", + "'!='", "'<'", + "'<='", "'>'", + "'>='", "'+'", + "'-'", "'*'", + "'/'", "'%'", + "'{'", "'}'", null, null, - null, "']'", + "']'", null, null, null, null, null, null, null, - null, null, - "'metadata'", + null, "'metadata'", null, null, null, null, null, null, @@ -233,7 +232,7 @@ export default class esql_lexer extends lexer_config { null, null, null, null, null, null, - "'USING'" ]; + "'join'", "'USING'" ]; public static readonly symbolicNames: (string | null)[] = [ null, "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", @@ -243,14 +242,13 @@ export default class esql_lexer extends lexer_config { "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", + "JOIN_LOOKUP", "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_METRICS", - "DEV_JOIN", "DEV_JOIN_FULL", "DEV_JOIN_LEFT", "DEV_JOIN_RIGHT", - "DEV_JOIN_LOOKUP", "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", @@ -326,7 +324,8 @@ export default class esql_lexer extends lexer_config { "LOOKUP_FIELD_LINE_COMMENT", "LOOKUP_FIELD_MULTILINE_COMMENT", "LOOKUP_FIELD_WS", - "USING", "JOIN_LINE_COMMENT", + "JOIN", "USING", + "JOIN_LINE_COMMENT", "JOIN_MULTILINE_COMMENT", "JOIN_WS", "METRICS_LINE_COMMENT", @@ -347,24 +346,24 @@ export default class esql_lexer extends lexer_config { public static readonly ruleNames: string[] = [ "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", "FROM", "GROK", "KEEP", "LIMIT", "MV_EXPAND", "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", - "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_METRICS", "DEV_JOIN", "DEV_JOIN_FULL", - "DEV_JOIN_LEFT", "DEV_JOIN_RIGHT", "DEV_JOIN_LOOKUP", "UNKNOWN_CMD", "LINE_COMMENT", - "MULTILINE_COMMENT", "WS", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", - "UNESCAPED_CHARS", "EXPONENT", "ASPERAND", "BACKQUOTE", "BACKQUOTE_BLOCK", - "UNDERSCORE", "UNQUOTED_ID_BODY", "QUOTED_STRING", "INTEGER_LITERAL", - "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", "CAST_OP", "COLON", "COMMA", - "DESC", "DOT", "FALSE", "FIRST", "IN", "IS", "LAST", "LIKE", "LP", "NOT", - "NULL", "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "EQ", "CIEQ", "NEQ", - "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "LEFT_BRACES", "RIGHT_BRACES", "NESTED_WHERE", "NAMED_OR_POSITIONAL_PARAM", - "OPENING_BRACKET", "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", "QUOTED_ID", - "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", - "EXPLAIN_OPENING_BRACKET", "EXPLAIN_PIPE", "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", - "EXPLAIN_MULTILINE_COMMENT", "FROM_PIPE", "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", - "FROM_COLON", "FROM_COMMA", "FROM_ASSIGN", "METADATA", "UNQUOTED_SOURCE_PART", - "UNQUOTED_SOURCE", "FROM_UNQUOTED_SOURCE", "FROM_QUOTED_SOURCE", "FROM_LINE_COMMENT", - "FROM_MULTILINE_COMMENT", "FROM_WS", "PROJECT_PIPE", "PROJECT_DOT", "PROJECT_COMMA", - "PROJECT_PARAM", "PROJECT_NAMED_OR_POSITIONAL_PARAM", "UNQUOTED_ID_BODY_WITH_PATTERN", + "JOIN_LOOKUP", "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_METRICS", "DEV_JOIN_FULL", + "DEV_JOIN_LEFT", "DEV_JOIN_RIGHT", "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", + "WS", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", + "EXPONENT", "ASPERAND", "BACKQUOTE", "BACKQUOTE_BLOCK", "UNDERSCORE", + "UNQUOTED_ID_BODY", "QUOTED_STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", + "BY", "AND", "ASC", "ASSIGN", "CAST_OP", "COLON", "COMMA", "DESC", "DOT", + "FALSE", "FIRST", "IN", "IS", "LAST", "LIKE", "LP", "NOT", "NULL", "NULLS", + "OR", "PARAM", "RLIKE", "RP", "TRUE", "EQ", "CIEQ", "NEQ", "LT", "LTE", + "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "LEFT_BRACES", + "RIGHT_BRACES", "NESTED_WHERE", "NAMED_OR_POSITIONAL_PARAM", "OPENING_BRACKET", + "CLOSING_BRACKET", "UNQUOTED_IDENTIFIER", "QUOTED_ID", "QUOTED_IDENTIFIER", + "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "EXPLAIN_OPENING_BRACKET", + "EXPLAIN_PIPE", "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", + "FROM_PIPE", "FROM_OPENING_BRACKET", "FROM_CLOSING_BRACKET", "FROM_COLON", + "FROM_COMMA", "FROM_ASSIGN", "METADATA", "UNQUOTED_SOURCE_PART", "UNQUOTED_SOURCE", + "FROM_UNQUOTED_SOURCE", "FROM_QUOTED_SOURCE", "FROM_LINE_COMMENT", "FROM_MULTILINE_COMMENT", + "FROM_WS", "PROJECT_PIPE", "PROJECT_DOT", "PROJECT_COMMA", "PROJECT_PARAM", + "PROJECT_NAMED_OR_POSITIONAL_PARAM", "UNQUOTED_ID_BODY_WITH_PATTERN", "UNQUOTED_ID_PATTERN", "ID_PATTERN", "PROJECT_LINE_COMMENT", "PROJECT_MULTILINE_COMMENT", "PROJECT_WS", "RENAME_PIPE", "RENAME_ASSIGN", "RENAME_COMMA", "RENAME_DOT", "RENAME_PARAM", "RENAME_NAMED_OR_POSITIONAL_PARAM", "AS", "RENAME_ID_PATTERN", @@ -384,7 +383,7 @@ export default class esql_lexer extends lexer_config { "LOOKUP_UNQUOTED_SOURCE", "LOOKUP_QUOTED_SOURCE", "LOOKUP_LINE_COMMENT", "LOOKUP_MULTILINE_COMMENT", "LOOKUP_WS", "LOOKUP_FIELD_PIPE", "LOOKUP_FIELD_COMMA", "LOOKUP_FIELD_DOT", "LOOKUP_FIELD_ID_PATTERN", "LOOKUP_FIELD_LINE_COMMENT", - "LOOKUP_FIELD_MULTILINE_COMMENT", "LOOKUP_FIELD_WS", "JOIN_PIPE", "JOIN_JOIN", + "LOOKUP_FIELD_MULTILINE_COMMENT", "LOOKUP_FIELD_WS", "JOIN_PIPE", "JOIN", "JOIN_AS", "JOIN_ON", "USING", "JOIN_UNQUOTED_SOURCE", "JOIN_QUOTED_SOURCE", "JOIN_COLON", "JOIN_UNQUOTED_IDENTIFER", "JOIN_QUOTED_IDENTIFIER", "JOIN_LINE_COMMENT", "JOIN_MULTILINE_COMMENT", "JOIN_WS", "METRICS_PIPE", "METRICS_UNQUOTED_SOURCE", @@ -415,41 +414,33 @@ export default class esql_lexer extends lexer_config { // @Override public sempred(localctx: RuleContext, ruleIndex: number, predIndex: number): boolean { switch (ruleIndex) { - case 16: - return this.DEV_INLINESTATS_sempred(localctx, predIndex); case 17: - return this.DEV_LOOKUP_sempred(localctx, predIndex); + return this.DEV_INLINESTATS_sempred(localctx, predIndex); case 18: - return this.DEV_METRICS_sempred(localctx, predIndex); + return this.DEV_LOOKUP_sempred(localctx, predIndex); case 19: - return this.DEV_JOIN_sempred(localctx, predIndex); + return this.DEV_METRICS_sempred(localctx, predIndex); case 20: return this.DEV_JOIN_FULL_sempred(localctx, predIndex); case 21: return this.DEV_JOIN_LEFT_sempred(localctx, predIndex); case 22: return this.DEV_JOIN_RIGHT_sempred(localctx, predIndex); - case 23: - return this.DEV_JOIN_LOOKUP_sempred(localctx, predIndex); - case 78: - return this.LEFT_BRACES_sempred(localctx, predIndex); - case 79: - return this.RIGHT_BRACES_sempred(localctx, predIndex); - case 112: + case 111: return this.PROJECT_PARAM_sempred(localctx, predIndex); - case 113: + case 112: return this.PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); - case 124: + case 123: return this.RENAME_PARAM_sempred(localctx, predIndex); - case 125: + case 124: return this.RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); - case 148: + case 147: return this.ENRICH_FIELD_PARAM_sempred(localctx, predIndex); - case 149: + case 148: return this.ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); - case 155: + case 154: return this.MVEXPAND_PARAM_sempred(localctx, predIndex); - case 156: + case 155: return this.MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx, predIndex); } return true; @@ -475,113 +466,85 @@ export default class esql_lexer extends lexer_config { } return true; } - private DEV_JOIN_sempred(localctx: RuleContext, predIndex: number): boolean { - switch (predIndex) { - case 3: - return this.isDevVersion(); - } - return true; - } private DEV_JOIN_FULL_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 4: + case 3: return this.isDevVersion(); } return true; } private DEV_JOIN_LEFT_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 5: + case 4: return this.isDevVersion(); } return true; } private DEV_JOIN_RIGHT_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 6: - return this.isDevVersion(); - } - return true; - } - private DEV_JOIN_LOOKUP_sempred(localctx: RuleContext, predIndex: number): boolean { - switch (predIndex) { - case 7: - return this.isDevVersion(); - } - return true; - } - private LEFT_BRACES_sempred(localctx: RuleContext, predIndex: number): boolean { - switch (predIndex) { - case 8: - return this.isDevVersion(); - } - return true; - } - private RIGHT_BRACES_sempred(localctx: RuleContext, predIndex: number): boolean { - switch (predIndex) { - case 9: + case 5: return this.isDevVersion(); } return true; } private PROJECT_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 10: + case 6: return this.isDevVersion(); } return true; } private PROJECT_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 11: + case 7: return this.isDevVersion(); } return true; } private RENAME_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 12: + case 8: return this.isDevVersion(); } return true; } private RENAME_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 13: + case 9: return this.isDevVersion(); } return true; } private ENRICH_FIELD_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 14: + case 10: return this.isDevVersion(); } return true; } private ENRICH_FIELD_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 15: + case 11: return this.isDevVersion(); } return true; } private MVEXPAND_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 16: + case 12: return this.isDevVersion(); } return true; } private MVEXPAND_NAMED_OR_POSITIONAL_PARAM_sempred(localctx: RuleContext, predIndex: number): boolean { switch (predIndex) { - case 17: + case 13: return this.isDevVersion(); } return true; } - public static readonly _serializedATN: number[] = [4,0,130,1629,6,-1,6, + public static readonly _serializedATN: number[] = [4,0,130,1617,6,-1,6, -1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1,6,-1, 2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8, 2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16, @@ -616,533 +579,529 @@ export default class esql_lexer extends lexer_config { 2,199,7,199,2,200,7,200,2,201,7,201,2,202,7,202,2,203,7,203,2,204,7,204, 2,205,7,205,2,206,7,206,2,207,7,207,2,208,7,208,2,209,7,209,2,210,7,210, 2,211,7,211,2,212,7,212,2,213,7,213,2,214,7,214,2,215,7,215,2,216,7,216, - 2,217,7,217,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3, - 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6, - 1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8, - 1,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1, - 10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,12,1,12, - 1,12,1,12,1,12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1, - 14,1,14,1,14,1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16, - 1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1, - 17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18, - 1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1, - 19,1,19,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,21, - 1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,1,23,1, - 23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,1,24,4,24,664,8,24,11,24,12,24,665, - 1,24,1,24,1,25,1,25,1,25,1,25,5,25,674,8,25,10,25,12,25,677,9,25,1,25,3, - 25,680,8,25,1,25,3,25,683,8,25,1,25,1,25,1,26,1,26,1,26,1,26,1,26,5,26, - 692,8,26,10,26,12,26,695,9,26,1,26,1,26,1,26,1,26,1,26,1,27,4,27,703,8, - 27,11,27,12,27,704,1,27,1,27,1,28,1,28,1,28,1,28,1,29,1,29,1,30,1,30,1, - 31,1,31,1,31,1,32,1,32,1,33,1,33,3,33,724,8,33,1,33,4,33,727,8,33,11,33, - 12,33,728,1,34,1,34,1,35,1,35,1,36,1,36,1,36,3,36,738,8,36,1,37,1,37,1, - 38,1,38,1,38,3,38,745,8,38,1,39,1,39,1,39,5,39,750,8,39,10,39,12,39,753, - 9,39,1,39,1,39,1,39,1,39,1,39,1,39,5,39,761,8,39,10,39,12,39,764,9,39,1, - 39,1,39,1,39,1,39,1,39,3,39,771,8,39,1,39,3,39,774,8,39,3,39,776,8,39,1, - 40,4,40,779,8,40,11,40,12,40,780,1,41,4,41,784,8,41,11,41,12,41,785,1,41, - 1,41,5,41,790,8,41,10,41,12,41,793,9,41,1,41,1,41,4,41,797,8,41,11,41,12, - 41,798,1,41,4,41,802,8,41,11,41,12,41,803,1,41,1,41,5,41,808,8,41,10,41, - 12,41,811,9,41,3,41,813,8,41,1,41,1,41,1,41,1,41,4,41,819,8,41,11,41,12, - 41,820,1,41,1,41,3,41,825,8,41,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,44, - 1,44,1,44,1,44,1,45,1,45,1,46,1,46,1,46,1,47,1,47,1,48,1,48,1,49,1,49,1, - 49,1,49,1,49,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52, - 1,52,1,52,1,53,1,53,1,53,1,54,1,54,1,54,1,55,1,55,1,55,1,55,1,55,1,56,1, - 56,1,56,1,56,1,56,1,57,1,57,1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59, - 1,60,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,62,1,62,1,63,1,63,1,63,1, - 63,1,63,1,63,1,64,1,64,1,65,1,65,1,65,1,65,1,65,1,66,1,66,1,66,1,67,1,67, - 1,67,1,68,1,68,1,68,1,69,1,69,1,70,1,70,1,70,1,71,1,71,1,72,1,72,1,72,1, - 73,1,73,1,74,1,74,1,75,1,75,1,76,1,76,1,77,1,77,1,78,1,78,1,78,1,79,1,79, - 1,79,1,80,1,80,1,80,1,80,1,81,1,81,1,81,3,81,959,8,81,1,81,5,81,962,8,81, - 10,81,12,81,965,9,81,1,81,1,81,4,81,969,8,81,11,81,12,81,970,3,81,973,8, - 81,1,82,1,82,1,82,1,82,1,82,1,83,1,83,1,83,1,83,1,83,1,84,1,84,5,84,987, - 8,84,10,84,12,84,990,9,84,1,84,1,84,3,84,994,8,84,1,84,4,84,997,8,84,11, - 84,12,84,998,3,84,1001,8,84,1,85,1,85,4,85,1005,8,85,11,85,12,85,1006,1, - 85,1,85,1,86,1,86,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,89,1,89,1,89, - 1,89,1,90,1,90,1,90,1,90,1,90,1,91,1,91,1,91,1,91,1,91,1,92,1,92,1,92,1, - 92,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1,95,1,95,1,95,1,95,1,95,1,96, - 1,96,1,96,1,96,1,97,1,97,1,97,1,97,1,98,1,98,1,98,1,98,1,99,1,99,1,99,1, - 99,1,100,1,100,1,100,1,100,1,101,1,101,1,101,1,101,1,101,1,101,1,101,1, - 101,1,101,1,102,1,102,1,102,3,102,1084,8,102,1,103,4,103,1087,8,103,11, - 103,12,103,1088,1,104,1,104,1,104,1,104,1,105,1,105,1,105,1,105,1,106,1, - 106,1,106,1,106,1,107,1,107,1,107,1,107,1,108,1,108,1,108,1,108,1,109,1, - 109,1,109,1,109,1,109,1,110,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1, - 112,1,112,1,112,1,112,1,112,1,113,1,113,1,113,1,113,1,113,1,114,1,114,1, - 114,1,114,3,114,1138,8,114,1,115,1,115,3,115,1142,8,115,1,115,5,115,1145, - 8,115,10,115,12,115,1148,9,115,1,115,1,115,3,115,1152,8,115,1,115,4,115, - 1155,8,115,11,115,12,115,1156,3,115,1159,8,115,1,116,1,116,4,116,1163,8, - 116,11,116,12,116,1164,1,117,1,117,1,117,1,117,1,118,1,118,1,118,1,118, - 1,119,1,119,1,119,1,119,1,120,1,120,1,120,1,120,1,120,1,121,1,121,1,121, - 1,121,1,122,1,122,1,122,1,122,1,123,1,123,1,123,1,123,1,124,1,124,1,124, - 1,124,1,124,1,125,1,125,1,125,1,125,1,125,1,126,1,126,1,126,1,127,1,127, - 1,127,1,127,1,128,1,128,1,128,1,128,1,129,1,129,1,129,1,129,1,130,1,130, - 1,130,1,130,1,131,1,131,1,131,1,131,1,131,1,132,1,132,1,132,1,132,1,132, - 1,133,1,133,1,133,1,133,1,133,1,134,1,134,1,134,1,134,1,134,1,134,1,134, - 1,135,1,135,1,136,4,136,1250,8,136,11,136,12,136,1251,1,136,1,136,3,136, - 1256,8,136,1,136,4,136,1259,8,136,11,136,12,136,1260,1,137,1,137,1,137, - 1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1,139,1,140,1,140,1,140, - 1,140,1,141,1,141,1,141,1,141,1,141,1,141,1,142,1,142,1,142,1,142,1,143, - 1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,145,1,145,1,145,1,145,1,146, - 1,146,1,146,1,146,1,147,1,147,1,147,1,147,1,148,1,148,1,148,1,148,1,148, - 1,149,1,149,1,149,1,149,1,149,1,150,1,150,1,150,1,150,1,151,1,151,1,151, - 1,151,1,152,1,152,1,152,1,152,1,153,1,153,1,153,1,153,1,153,1,154,1,154, - 1,154,1,154,1,155,1,155,1,155,1,155,1,155,1,156,1,156,1,156,1,156,1,156, - 1,157,1,157,1,157,1,157,1,158,1,158,1,158,1,158,1,159,1,159,1,159,1,159, - 1,160,1,160,1,160,1,160,1,161,1,161,1,161,1,161,1,162,1,162,1,162,1,162, - 1,162,1,163,1,163,1,163,1,163,1,163,1,164,1,164,1,164,1,164,1,165,1,165, - 1,165,1,165,1,166,1,166,1,166,1,166,1,167,1,167,1,167,1,167,1,167,1,168, - 1,168,1,168,1,168,1,169,1,169,1,169,1,169,1,169,4,169,1406,8,169,11,169, - 12,169,1407,1,170,1,170,1,170,1,170,1,171,1,171,1,171,1,171,1,172,1,172, - 1,172,1,172,1,173,1,173,1,173,1,173,1,173,1,174,1,174,1,174,1,174,1,175, - 1,175,1,175,1,175,1,176,1,176,1,176,1,176,1,177,1,177,1,177,1,177,1,177, - 1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179,1,180,1,180,1,180,1,180, - 1,181,1,181,1,181,1,181,1,182,1,182,1,182,1,182,1,183,1,183,1,183,1,183, - 1,183,1,183,1,184,1,184,1,184,1,184,1,185,1,185,1,185,1,185,1,186,1,186, - 1,186,1,186,1,187,1,187,1,187,1,187,1,188,1,188,1,188,1,188,1,189,1,189, - 1,189,1,189,1,190,1,190,1,190,1,190,1,190,1,191,1,191,1,191,1,191,1,192, - 1,192,1,192,1,192,1,193,1,193,1,193,1,193,1,193,1,193,1,194,1,194,1,194, - 1,194,1,194,1,194,1,194,1,194,1,194,1,195,1,195,1,195,1,195,1,196,1,196, - 1,196,1,196,1,197,1,197,1,197,1,197,1,198,1,198,1,198,1,198,1,199,1,199, - 1,199,1,199,1,200,1,200,1,200,1,200,1,201,1,201,1,201,1,201,1,202,1,202, - 1,202,1,202,1,203,1,203,1,203,1,203,1,203,1,204,1,204,1,204,1,204,1,204, - 1,204,1,205,1,205,1,205,1,205,1,205,1,205,1,206,1,206,1,206,1,206,1,207, - 1,207,1,207,1,207,1,208,1,208,1,208,1,208,1,209,1,209,1,209,1,209,1,209, - 1,209,1,210,1,210,1,210,1,210,1,210,1,210,1,211,1,211,1,211,1,211,1,212, - 1,212,1,212,1,212,1,213,1,213,1,213,1,213,1,214,1,214,1,214,1,214,1,214, - 1,214,1,215,1,215,1,215,1,215,1,215,1,215,1,216,1,216,1,216,1,216,1,216, - 1,216,1,217,1,217,1,217,1,217,1,217,2,693,762,0,218,16,1,18,2,20,3,22,4, - 24,5,26,6,28,7,30,8,32,9,34,10,36,11,38,12,40,13,42,14,44,15,46,16,48,17, - 50,18,52,19,54,20,56,21,58,22,60,23,62,24,64,25,66,26,68,27,70,28,72,29, - 74,0,76,0,78,0,80,0,82,0,84,0,86,0,88,0,90,0,92,0,94,30,96,31,98,32,100, - 33,102,34,104,35,106,36,108,37,110,38,112,39,114,40,116,41,118,42,120,43, - 122,44,124,45,126,46,128,47,130,48,132,49,134,50,136,51,138,52,140,53,142, - 54,144,55,146,56,148,57,150,58,152,59,154,60,156,61,158,62,160,63,162,64, - 164,65,166,66,168,67,170,68,172,69,174,70,176,0,178,71,180,72,182,73,184, - 74,186,0,188,75,190,76,192,77,194,78,196,0,198,0,200,79,202,80,204,81,206, - 0,208,0,210,0,212,0,214,0,216,0,218,82,220,0,222,83,224,0,226,0,228,84, - 230,85,232,86,234,0,236,0,238,0,240,0,242,0,244,0,246,0,248,87,250,88,252, - 89,254,90,256,0,258,0,260,0,262,0,264,0,266,0,268,91,270,0,272,92,274,93, - 276,94,278,0,280,0,282,95,284,96,286,0,288,97,290,0,292,98,294,99,296,100, - 298,0,300,0,302,0,304,0,306,0,308,0,310,0,312,0,314,0,316,101,318,102,320, - 103,322,0,324,0,326,0,328,0,330,0,332,0,334,104,336,105,338,106,340,0,342, - 107,344,108,346,109,348,110,350,0,352,0,354,111,356,112,358,113,360,114, - 362,0,364,0,366,0,368,0,370,0,372,0,374,0,376,115,378,116,380,117,382,0, - 384,0,386,0,388,0,390,118,392,119,394,120,396,0,398,0,400,0,402,0,404,121, - 406,0,408,0,410,0,412,0,414,0,416,122,418,123,420,124,422,0,424,0,426,0, - 428,125,430,126,432,127,434,0,436,0,438,128,440,129,442,130,444,0,446,0, - 448,0,450,0,16,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,36,2,0,68,68,100,100, - 2,0,73,73,105,105,2,0,83,83,115,115,2,0,69,69,101,101,2,0,67,67,99,99,2, - 0,84,84,116,116,2,0,82,82,114,114,2,0,79,79,111,111,2,0,80,80,112,112,2, - 0,78,78,110,110,2,0,72,72,104,104,2,0,86,86,118,118,2,0,65,65,97,97,2,0, - 76,76,108,108,2,0,88,88,120,120,2,0,70,70,102,102,2,0,77,77,109,109,2,0, - 71,71,103,103,2,0,75,75,107,107,2,0,87,87,119,119,2,0,85,85,117,117,2,0, - 74,74,106,106,6,0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13,3, - 0,9,10,13,13,32,32,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82,84, - 84,92,92,110,110,114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43,43, - 45,45,1,0,96,96,2,0,66,66,98,98,2,0,89,89,121,121,11,0,9,10,13,13,32,32, - 34,34,44,44,47,47,58,58,61,61,91,91,93,93,124,124,2,0,42,42,47,47,11,0, - 9,10,13,13,32,32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124,1656, - 0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0,0,0,24,1,0,0,0,0,26,1, - 0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34,1,0,0,0,0,36,1,0,0,0, - 0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0,0,0,46,1,0,0,0,0,48,1, - 0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,0,56,1,0,0,0,0,58,1,0,0,0, - 0,60,1,0,0,0,0,62,1,0,0,0,0,64,1,0,0,0,0,66,1,0,0,0,0,68,1,0,0,0,0,70,1, - 0,0,0,1,72,1,0,0,0,1,94,1,0,0,0,1,96,1,0,0,0,1,98,1,0,0,0,1,100,1,0,0,0, - 1,102,1,0,0,0,1,104,1,0,0,0,1,106,1,0,0,0,1,108,1,0,0,0,1,110,1,0,0,0,1, - 112,1,0,0,0,1,114,1,0,0,0,1,116,1,0,0,0,1,118,1,0,0,0,1,120,1,0,0,0,1,122, - 1,0,0,0,1,124,1,0,0,0,1,126,1,0,0,0,1,128,1,0,0,0,1,130,1,0,0,0,1,132,1, - 0,0,0,1,134,1,0,0,0,1,136,1,0,0,0,1,138,1,0,0,0,1,140,1,0,0,0,1,142,1,0, - 0,0,1,144,1,0,0,0,1,146,1,0,0,0,1,148,1,0,0,0,1,150,1,0,0,0,1,152,1,0,0, - 0,1,154,1,0,0,0,1,156,1,0,0,0,1,158,1,0,0,0,1,160,1,0,0,0,1,162,1,0,0,0, - 1,164,1,0,0,0,1,166,1,0,0,0,1,168,1,0,0,0,1,170,1,0,0,0,1,172,1,0,0,0,1, - 174,1,0,0,0,1,176,1,0,0,0,1,178,1,0,0,0,1,180,1,0,0,0,1,182,1,0,0,0,1,184, - 1,0,0,0,1,188,1,0,0,0,1,190,1,0,0,0,1,192,1,0,0,0,1,194,1,0,0,0,2,196,1, - 0,0,0,2,198,1,0,0,0,2,200,1,0,0,0,2,202,1,0,0,0,2,204,1,0,0,0,3,206,1,0, - 0,0,3,208,1,0,0,0,3,210,1,0,0,0,3,212,1,0,0,0,3,214,1,0,0,0,3,216,1,0,0, - 0,3,218,1,0,0,0,3,222,1,0,0,0,3,224,1,0,0,0,3,226,1,0,0,0,3,228,1,0,0,0, - 3,230,1,0,0,0,3,232,1,0,0,0,4,234,1,0,0,0,4,236,1,0,0,0,4,238,1,0,0,0,4, - 240,1,0,0,0,4,242,1,0,0,0,4,248,1,0,0,0,4,250,1,0,0,0,4,252,1,0,0,0,4,254, - 1,0,0,0,5,256,1,0,0,0,5,258,1,0,0,0,5,260,1,0,0,0,5,262,1,0,0,0,5,264,1, - 0,0,0,5,266,1,0,0,0,5,268,1,0,0,0,5,270,1,0,0,0,5,272,1,0,0,0,5,274,1,0, - 0,0,5,276,1,0,0,0,6,278,1,0,0,0,6,280,1,0,0,0,6,282,1,0,0,0,6,284,1,0,0, - 0,6,288,1,0,0,0,6,290,1,0,0,0,6,292,1,0,0,0,6,294,1,0,0,0,6,296,1,0,0,0, - 7,298,1,0,0,0,7,300,1,0,0,0,7,302,1,0,0,0,7,304,1,0,0,0,7,306,1,0,0,0,7, - 308,1,0,0,0,7,310,1,0,0,0,7,312,1,0,0,0,7,314,1,0,0,0,7,316,1,0,0,0,7,318, - 1,0,0,0,7,320,1,0,0,0,8,322,1,0,0,0,8,324,1,0,0,0,8,326,1,0,0,0,8,328,1, - 0,0,0,8,330,1,0,0,0,8,332,1,0,0,0,8,334,1,0,0,0,8,336,1,0,0,0,8,338,1,0, - 0,0,9,340,1,0,0,0,9,342,1,0,0,0,9,344,1,0,0,0,9,346,1,0,0,0,9,348,1,0,0, - 0,10,350,1,0,0,0,10,352,1,0,0,0,10,354,1,0,0,0,10,356,1,0,0,0,10,358,1, - 0,0,0,10,360,1,0,0,0,11,362,1,0,0,0,11,364,1,0,0,0,11,366,1,0,0,0,11,368, - 1,0,0,0,11,370,1,0,0,0,11,372,1,0,0,0,11,374,1,0,0,0,11,376,1,0,0,0,11, - 378,1,0,0,0,11,380,1,0,0,0,12,382,1,0,0,0,12,384,1,0,0,0,12,386,1,0,0,0, - 12,388,1,0,0,0,12,390,1,0,0,0,12,392,1,0,0,0,12,394,1,0,0,0,13,396,1,0, - 0,0,13,398,1,0,0,0,13,400,1,0,0,0,13,402,1,0,0,0,13,404,1,0,0,0,13,406, - 1,0,0,0,13,408,1,0,0,0,13,410,1,0,0,0,13,412,1,0,0,0,13,414,1,0,0,0,13, - 416,1,0,0,0,13,418,1,0,0,0,13,420,1,0,0,0,14,422,1,0,0,0,14,424,1,0,0,0, - 14,426,1,0,0,0,14,428,1,0,0,0,14,430,1,0,0,0,14,432,1,0,0,0,15,434,1,0, - 0,0,15,436,1,0,0,0,15,438,1,0,0,0,15,440,1,0,0,0,15,442,1,0,0,0,15,444, - 1,0,0,0,15,446,1,0,0,0,15,448,1,0,0,0,15,450,1,0,0,0,16,452,1,0,0,0,18, - 462,1,0,0,0,20,469,1,0,0,0,22,478,1,0,0,0,24,485,1,0,0,0,26,495,1,0,0,0, - 28,502,1,0,0,0,30,509,1,0,0,0,32,516,1,0,0,0,34,524,1,0,0,0,36,536,1,0, - 0,0,38,545,1,0,0,0,40,551,1,0,0,0,42,558,1,0,0,0,44,565,1,0,0,0,46,573, - 1,0,0,0,48,581,1,0,0,0,50,596,1,0,0,0,52,608,1,0,0,0,54,619,1,0,0,0,56, - 627,1,0,0,0,58,635,1,0,0,0,60,643,1,0,0,0,62,652,1,0,0,0,64,663,1,0,0,0, - 66,669,1,0,0,0,68,686,1,0,0,0,70,702,1,0,0,0,72,708,1,0,0,0,74,712,1,0, - 0,0,76,714,1,0,0,0,78,716,1,0,0,0,80,719,1,0,0,0,82,721,1,0,0,0,84,730, - 1,0,0,0,86,732,1,0,0,0,88,737,1,0,0,0,90,739,1,0,0,0,92,744,1,0,0,0,94, - 775,1,0,0,0,96,778,1,0,0,0,98,824,1,0,0,0,100,826,1,0,0,0,102,829,1,0,0, - 0,104,833,1,0,0,0,106,837,1,0,0,0,108,839,1,0,0,0,110,842,1,0,0,0,112,844, - 1,0,0,0,114,846,1,0,0,0,116,851,1,0,0,0,118,853,1,0,0,0,120,859,1,0,0,0, - 122,865,1,0,0,0,124,868,1,0,0,0,126,871,1,0,0,0,128,876,1,0,0,0,130,881, - 1,0,0,0,132,883,1,0,0,0,134,887,1,0,0,0,136,892,1,0,0,0,138,898,1,0,0,0, - 140,901,1,0,0,0,142,903,1,0,0,0,144,909,1,0,0,0,146,911,1,0,0,0,148,916, - 1,0,0,0,150,919,1,0,0,0,152,922,1,0,0,0,154,925,1,0,0,0,156,927,1,0,0,0, - 158,930,1,0,0,0,160,932,1,0,0,0,162,935,1,0,0,0,164,937,1,0,0,0,166,939, - 1,0,0,0,168,941,1,0,0,0,170,943,1,0,0,0,172,945,1,0,0,0,174,948,1,0,0,0, - 176,951,1,0,0,0,178,972,1,0,0,0,180,974,1,0,0,0,182,979,1,0,0,0,184,1000, - 1,0,0,0,186,1002,1,0,0,0,188,1010,1,0,0,0,190,1012,1,0,0,0,192,1016,1,0, - 0,0,194,1020,1,0,0,0,196,1024,1,0,0,0,198,1029,1,0,0,0,200,1034,1,0,0,0, - 202,1038,1,0,0,0,204,1042,1,0,0,0,206,1046,1,0,0,0,208,1051,1,0,0,0,210, - 1055,1,0,0,0,212,1059,1,0,0,0,214,1063,1,0,0,0,216,1067,1,0,0,0,218,1071, - 1,0,0,0,220,1083,1,0,0,0,222,1086,1,0,0,0,224,1090,1,0,0,0,226,1094,1,0, - 0,0,228,1098,1,0,0,0,230,1102,1,0,0,0,232,1106,1,0,0,0,234,1110,1,0,0,0, - 236,1115,1,0,0,0,238,1119,1,0,0,0,240,1123,1,0,0,0,242,1128,1,0,0,0,244, - 1137,1,0,0,0,246,1158,1,0,0,0,248,1162,1,0,0,0,250,1166,1,0,0,0,252,1170, - 1,0,0,0,254,1174,1,0,0,0,256,1178,1,0,0,0,258,1183,1,0,0,0,260,1187,1,0, - 0,0,262,1191,1,0,0,0,264,1195,1,0,0,0,266,1200,1,0,0,0,268,1205,1,0,0,0, - 270,1208,1,0,0,0,272,1212,1,0,0,0,274,1216,1,0,0,0,276,1220,1,0,0,0,278, - 1224,1,0,0,0,280,1229,1,0,0,0,282,1234,1,0,0,0,284,1239,1,0,0,0,286,1246, - 1,0,0,0,288,1255,1,0,0,0,290,1262,1,0,0,0,292,1266,1,0,0,0,294,1270,1,0, - 0,0,296,1274,1,0,0,0,298,1278,1,0,0,0,300,1284,1,0,0,0,302,1288,1,0,0,0, - 304,1292,1,0,0,0,306,1296,1,0,0,0,308,1300,1,0,0,0,310,1304,1,0,0,0,312, - 1308,1,0,0,0,314,1313,1,0,0,0,316,1318,1,0,0,0,318,1322,1,0,0,0,320,1326, - 1,0,0,0,322,1330,1,0,0,0,324,1335,1,0,0,0,326,1339,1,0,0,0,328,1344,1,0, - 0,0,330,1349,1,0,0,0,332,1353,1,0,0,0,334,1357,1,0,0,0,336,1361,1,0,0,0, - 338,1365,1,0,0,0,340,1369,1,0,0,0,342,1374,1,0,0,0,344,1379,1,0,0,0,346, - 1383,1,0,0,0,348,1387,1,0,0,0,350,1391,1,0,0,0,352,1396,1,0,0,0,354,1405, - 1,0,0,0,356,1409,1,0,0,0,358,1413,1,0,0,0,360,1417,1,0,0,0,362,1421,1,0, - 0,0,364,1426,1,0,0,0,366,1430,1,0,0,0,368,1434,1,0,0,0,370,1438,1,0,0,0, - 372,1443,1,0,0,0,374,1447,1,0,0,0,376,1451,1,0,0,0,378,1455,1,0,0,0,380, - 1459,1,0,0,0,382,1463,1,0,0,0,384,1469,1,0,0,0,386,1473,1,0,0,0,388,1477, - 1,0,0,0,390,1481,1,0,0,0,392,1485,1,0,0,0,394,1489,1,0,0,0,396,1493,1,0, - 0,0,398,1498,1,0,0,0,400,1502,1,0,0,0,402,1506,1,0,0,0,404,1512,1,0,0,0, - 406,1521,1,0,0,0,408,1525,1,0,0,0,410,1529,1,0,0,0,412,1533,1,0,0,0,414, - 1537,1,0,0,0,416,1541,1,0,0,0,418,1545,1,0,0,0,420,1549,1,0,0,0,422,1553, - 1,0,0,0,424,1558,1,0,0,0,426,1564,1,0,0,0,428,1570,1,0,0,0,430,1574,1,0, - 0,0,432,1578,1,0,0,0,434,1582,1,0,0,0,436,1588,1,0,0,0,438,1594,1,0,0,0, - 440,1598,1,0,0,0,442,1602,1,0,0,0,444,1606,1,0,0,0,446,1612,1,0,0,0,448, - 1618,1,0,0,0,450,1624,1,0,0,0,452,453,7,0,0,0,453,454,7,1,0,0,454,455,7, - 2,0,0,455,456,7,2,0,0,456,457,7,3,0,0,457,458,7,4,0,0,458,459,7,5,0,0,459, - 460,1,0,0,0,460,461,6,0,0,0,461,17,1,0,0,0,462,463,7,0,0,0,463,464,7,6, - 0,0,464,465,7,7,0,0,465,466,7,8,0,0,466,467,1,0,0,0,467,468,6,1,1,0,468, - 19,1,0,0,0,469,470,7,3,0,0,470,471,7,9,0,0,471,472,7,6,0,0,472,473,7,1, - 0,0,473,474,7,4,0,0,474,475,7,10,0,0,475,476,1,0,0,0,476,477,6,2,2,0,477, - 21,1,0,0,0,478,479,7,3,0,0,479,480,7,11,0,0,480,481,7,12,0,0,481,482,7, - 13,0,0,482,483,1,0,0,0,483,484,6,3,0,0,484,23,1,0,0,0,485,486,7,3,0,0,486, - 487,7,14,0,0,487,488,7,8,0,0,488,489,7,13,0,0,489,490,7,12,0,0,490,491, - 7,1,0,0,491,492,7,9,0,0,492,493,1,0,0,0,493,494,6,4,3,0,494,25,1,0,0,0, - 495,496,7,15,0,0,496,497,7,6,0,0,497,498,7,7,0,0,498,499,7,16,0,0,499,500, - 1,0,0,0,500,501,6,5,4,0,501,27,1,0,0,0,502,503,7,17,0,0,503,504,7,6,0,0, - 504,505,7,7,0,0,505,506,7,18,0,0,506,507,1,0,0,0,507,508,6,6,0,0,508,29, - 1,0,0,0,509,510,7,18,0,0,510,511,7,3,0,0,511,512,7,3,0,0,512,513,7,8,0, - 0,513,514,1,0,0,0,514,515,6,7,1,0,515,31,1,0,0,0,516,517,7,13,0,0,517,518, - 7,1,0,0,518,519,7,16,0,0,519,520,7,1,0,0,520,521,7,5,0,0,521,522,1,0,0, - 0,522,523,6,8,0,0,523,33,1,0,0,0,524,525,7,16,0,0,525,526,7,11,0,0,526, - 527,5,95,0,0,527,528,7,3,0,0,528,529,7,14,0,0,529,530,7,8,0,0,530,531,7, - 12,0,0,531,532,7,9,0,0,532,533,7,0,0,0,533,534,1,0,0,0,534,535,6,9,5,0, - 535,35,1,0,0,0,536,537,7,6,0,0,537,538,7,3,0,0,538,539,7,9,0,0,539,540, - 7,12,0,0,540,541,7,16,0,0,541,542,7,3,0,0,542,543,1,0,0,0,543,544,6,10, - 6,0,544,37,1,0,0,0,545,546,7,6,0,0,546,547,7,7,0,0,547,548,7,19,0,0,548, - 549,1,0,0,0,549,550,6,11,0,0,550,39,1,0,0,0,551,552,7,2,0,0,552,553,7,10, - 0,0,553,554,7,7,0,0,554,555,7,19,0,0,555,556,1,0,0,0,556,557,6,12,7,0,557, - 41,1,0,0,0,558,559,7,2,0,0,559,560,7,7,0,0,560,561,7,6,0,0,561,562,7,5, - 0,0,562,563,1,0,0,0,563,564,6,13,0,0,564,43,1,0,0,0,565,566,7,2,0,0,566, - 567,7,5,0,0,567,568,7,12,0,0,568,569,7,5,0,0,569,570,7,2,0,0,570,571,1, - 0,0,0,571,572,6,14,0,0,572,45,1,0,0,0,573,574,7,19,0,0,574,575,7,10,0,0, - 575,576,7,3,0,0,576,577,7,6,0,0,577,578,7,3,0,0,578,579,1,0,0,0,579,580, - 6,15,0,0,580,47,1,0,0,0,581,582,4,16,0,0,582,583,7,1,0,0,583,584,7,9,0, - 0,584,585,7,13,0,0,585,586,7,1,0,0,586,587,7,9,0,0,587,588,7,3,0,0,588, - 589,7,2,0,0,589,590,7,5,0,0,590,591,7,12,0,0,591,592,7,5,0,0,592,593,7, - 2,0,0,593,594,1,0,0,0,594,595,6,16,0,0,595,49,1,0,0,0,596,597,4,17,1,0, - 597,598,7,13,0,0,598,599,7,7,0,0,599,600,7,7,0,0,600,601,7,18,0,0,601,602, - 7,20,0,0,602,603,7,8,0,0,603,604,5,95,0,0,604,605,5,128020,0,0,605,606, - 1,0,0,0,606,607,6,17,8,0,607,51,1,0,0,0,608,609,4,18,2,0,609,610,7,16,0, - 0,610,611,7,3,0,0,611,612,7,5,0,0,612,613,7,6,0,0,613,614,7,1,0,0,614,615, - 7,4,0,0,615,616,7,2,0,0,616,617,1,0,0,0,617,618,6,18,9,0,618,53,1,0,0,0, - 619,620,4,19,3,0,620,621,7,21,0,0,621,622,7,7,0,0,622,623,7,1,0,0,623,624, - 7,9,0,0,624,625,1,0,0,0,625,626,6,19,10,0,626,55,1,0,0,0,627,628,4,20,4, - 0,628,629,7,15,0,0,629,630,7,20,0,0,630,631,7,13,0,0,631,632,7,13,0,0,632, - 633,1,0,0,0,633,634,6,20,10,0,634,57,1,0,0,0,635,636,4,21,5,0,636,637,7, - 13,0,0,637,638,7,3,0,0,638,639,7,15,0,0,639,640,7,5,0,0,640,641,1,0,0,0, - 641,642,6,21,10,0,642,59,1,0,0,0,643,644,4,22,6,0,644,645,7,6,0,0,645,646, - 7,1,0,0,646,647,7,17,0,0,647,648,7,10,0,0,648,649,7,5,0,0,649,650,1,0,0, - 0,650,651,6,22,10,0,651,61,1,0,0,0,652,653,4,23,7,0,653,654,7,13,0,0,654, - 655,7,7,0,0,655,656,7,7,0,0,656,657,7,18,0,0,657,658,7,20,0,0,658,659,7, - 8,0,0,659,660,1,0,0,0,660,661,6,23,10,0,661,63,1,0,0,0,662,664,8,22,0,0, - 663,662,1,0,0,0,664,665,1,0,0,0,665,663,1,0,0,0,665,666,1,0,0,0,666,667, - 1,0,0,0,667,668,6,24,0,0,668,65,1,0,0,0,669,670,5,47,0,0,670,671,5,47,0, - 0,671,675,1,0,0,0,672,674,8,23,0,0,673,672,1,0,0,0,674,677,1,0,0,0,675, - 673,1,0,0,0,675,676,1,0,0,0,676,679,1,0,0,0,677,675,1,0,0,0,678,680,5,13, - 0,0,679,678,1,0,0,0,679,680,1,0,0,0,680,682,1,0,0,0,681,683,5,10,0,0,682, - 681,1,0,0,0,682,683,1,0,0,0,683,684,1,0,0,0,684,685,6,25,11,0,685,67,1, - 0,0,0,686,687,5,47,0,0,687,688,5,42,0,0,688,693,1,0,0,0,689,692,3,68,26, - 0,690,692,9,0,0,0,691,689,1,0,0,0,691,690,1,0,0,0,692,695,1,0,0,0,693,694, - 1,0,0,0,693,691,1,0,0,0,694,696,1,0,0,0,695,693,1,0,0,0,696,697,5,42,0, - 0,697,698,5,47,0,0,698,699,1,0,0,0,699,700,6,26,11,0,700,69,1,0,0,0,701, - 703,7,24,0,0,702,701,1,0,0,0,703,704,1,0,0,0,704,702,1,0,0,0,704,705,1, - 0,0,0,705,706,1,0,0,0,706,707,6,27,11,0,707,71,1,0,0,0,708,709,5,124,0, - 0,709,710,1,0,0,0,710,711,6,28,12,0,711,73,1,0,0,0,712,713,7,25,0,0,713, - 75,1,0,0,0,714,715,7,26,0,0,715,77,1,0,0,0,716,717,5,92,0,0,717,718,7,27, - 0,0,718,79,1,0,0,0,719,720,8,28,0,0,720,81,1,0,0,0,721,723,7,3,0,0,722, - 724,7,29,0,0,723,722,1,0,0,0,723,724,1,0,0,0,724,726,1,0,0,0,725,727,3, - 74,29,0,726,725,1,0,0,0,727,728,1,0,0,0,728,726,1,0,0,0,728,729,1,0,0,0, - 729,83,1,0,0,0,730,731,5,64,0,0,731,85,1,0,0,0,732,733,5,96,0,0,733,87, - 1,0,0,0,734,738,8,30,0,0,735,736,5,96,0,0,736,738,5,96,0,0,737,734,1,0, - 0,0,737,735,1,0,0,0,738,89,1,0,0,0,739,740,5,95,0,0,740,91,1,0,0,0,741, - 745,3,76,30,0,742,745,3,74,29,0,743,745,3,90,37,0,744,741,1,0,0,0,744,742, - 1,0,0,0,744,743,1,0,0,0,745,93,1,0,0,0,746,751,5,34,0,0,747,750,3,78,31, - 0,748,750,3,80,32,0,749,747,1,0,0,0,749,748,1,0,0,0,750,753,1,0,0,0,751, - 749,1,0,0,0,751,752,1,0,0,0,752,754,1,0,0,0,753,751,1,0,0,0,754,776,5,34, - 0,0,755,756,5,34,0,0,756,757,5,34,0,0,757,758,5,34,0,0,758,762,1,0,0,0, - 759,761,8,23,0,0,760,759,1,0,0,0,761,764,1,0,0,0,762,763,1,0,0,0,762,760, - 1,0,0,0,763,765,1,0,0,0,764,762,1,0,0,0,765,766,5,34,0,0,766,767,5,34,0, - 0,767,768,5,34,0,0,768,770,1,0,0,0,769,771,5,34,0,0,770,769,1,0,0,0,770, - 771,1,0,0,0,771,773,1,0,0,0,772,774,5,34,0,0,773,772,1,0,0,0,773,774,1, - 0,0,0,774,776,1,0,0,0,775,746,1,0,0,0,775,755,1,0,0,0,776,95,1,0,0,0,777, - 779,3,74,29,0,778,777,1,0,0,0,779,780,1,0,0,0,780,778,1,0,0,0,780,781,1, - 0,0,0,781,97,1,0,0,0,782,784,3,74,29,0,783,782,1,0,0,0,784,785,1,0,0,0, - 785,783,1,0,0,0,785,786,1,0,0,0,786,787,1,0,0,0,787,791,3,116,50,0,788, - 790,3,74,29,0,789,788,1,0,0,0,790,793,1,0,0,0,791,789,1,0,0,0,791,792,1, - 0,0,0,792,825,1,0,0,0,793,791,1,0,0,0,794,796,3,116,50,0,795,797,3,74,29, - 0,796,795,1,0,0,0,797,798,1,0,0,0,798,796,1,0,0,0,798,799,1,0,0,0,799,825, - 1,0,0,0,800,802,3,74,29,0,801,800,1,0,0,0,802,803,1,0,0,0,803,801,1,0,0, - 0,803,804,1,0,0,0,804,812,1,0,0,0,805,809,3,116,50,0,806,808,3,74,29,0, - 807,806,1,0,0,0,808,811,1,0,0,0,809,807,1,0,0,0,809,810,1,0,0,0,810,813, - 1,0,0,0,811,809,1,0,0,0,812,805,1,0,0,0,812,813,1,0,0,0,813,814,1,0,0,0, - 814,815,3,82,33,0,815,825,1,0,0,0,816,818,3,116,50,0,817,819,3,74,29,0, - 818,817,1,0,0,0,819,820,1,0,0,0,820,818,1,0,0,0,820,821,1,0,0,0,821,822, - 1,0,0,0,822,823,3,82,33,0,823,825,1,0,0,0,824,783,1,0,0,0,824,794,1,0,0, - 0,824,801,1,0,0,0,824,816,1,0,0,0,825,99,1,0,0,0,826,827,7,31,0,0,827,828, - 7,32,0,0,828,101,1,0,0,0,829,830,7,12,0,0,830,831,7,9,0,0,831,832,7,0,0, - 0,832,103,1,0,0,0,833,834,7,12,0,0,834,835,7,2,0,0,835,836,7,4,0,0,836, - 105,1,0,0,0,837,838,5,61,0,0,838,107,1,0,0,0,839,840,5,58,0,0,840,841,5, - 58,0,0,841,109,1,0,0,0,842,843,5,58,0,0,843,111,1,0,0,0,844,845,5,44,0, - 0,845,113,1,0,0,0,846,847,7,0,0,0,847,848,7,3,0,0,848,849,7,2,0,0,849,850, - 7,4,0,0,850,115,1,0,0,0,851,852,5,46,0,0,852,117,1,0,0,0,853,854,7,15,0, - 0,854,855,7,12,0,0,855,856,7,13,0,0,856,857,7,2,0,0,857,858,7,3,0,0,858, - 119,1,0,0,0,859,860,7,15,0,0,860,861,7,1,0,0,861,862,7,6,0,0,862,863,7, - 2,0,0,863,864,7,5,0,0,864,121,1,0,0,0,865,866,7,1,0,0,866,867,7,9,0,0,867, - 123,1,0,0,0,868,869,7,1,0,0,869,870,7,2,0,0,870,125,1,0,0,0,871,872,7,13, - 0,0,872,873,7,12,0,0,873,874,7,2,0,0,874,875,7,5,0,0,875,127,1,0,0,0,876, - 877,7,13,0,0,877,878,7,1,0,0,878,879,7,18,0,0,879,880,7,3,0,0,880,129,1, - 0,0,0,881,882,5,40,0,0,882,131,1,0,0,0,883,884,7,9,0,0,884,885,7,7,0,0, - 885,886,7,5,0,0,886,133,1,0,0,0,887,888,7,9,0,0,888,889,7,20,0,0,889,890, - 7,13,0,0,890,891,7,13,0,0,891,135,1,0,0,0,892,893,7,9,0,0,893,894,7,20, - 0,0,894,895,7,13,0,0,895,896,7,13,0,0,896,897,7,2,0,0,897,137,1,0,0,0,898, - 899,7,7,0,0,899,900,7,6,0,0,900,139,1,0,0,0,901,902,5,63,0,0,902,141,1, - 0,0,0,903,904,7,6,0,0,904,905,7,13,0,0,905,906,7,1,0,0,906,907,7,18,0,0, - 907,908,7,3,0,0,908,143,1,0,0,0,909,910,5,41,0,0,910,145,1,0,0,0,911,912, - 7,5,0,0,912,913,7,6,0,0,913,914,7,20,0,0,914,915,7,3,0,0,915,147,1,0,0, - 0,916,917,5,61,0,0,917,918,5,61,0,0,918,149,1,0,0,0,919,920,5,61,0,0,920, - 921,5,126,0,0,921,151,1,0,0,0,922,923,5,33,0,0,923,924,5,61,0,0,924,153, - 1,0,0,0,925,926,5,60,0,0,926,155,1,0,0,0,927,928,5,60,0,0,928,929,5,61, - 0,0,929,157,1,0,0,0,930,931,5,62,0,0,931,159,1,0,0,0,932,933,5,62,0,0,933, - 934,5,61,0,0,934,161,1,0,0,0,935,936,5,43,0,0,936,163,1,0,0,0,937,938,5, - 45,0,0,938,165,1,0,0,0,939,940,5,42,0,0,940,167,1,0,0,0,941,942,5,47,0, - 0,942,169,1,0,0,0,943,944,5,37,0,0,944,171,1,0,0,0,945,946,4,78,8,0,946, - 947,5,123,0,0,947,173,1,0,0,0,948,949,4,79,9,0,949,950,5,125,0,0,950,175, - 1,0,0,0,951,952,3,46,15,0,952,953,1,0,0,0,953,954,6,80,13,0,954,177,1,0, - 0,0,955,958,3,140,62,0,956,959,3,76,30,0,957,959,3,90,37,0,958,956,1,0, - 0,0,958,957,1,0,0,0,959,963,1,0,0,0,960,962,3,92,38,0,961,960,1,0,0,0,962, - 965,1,0,0,0,963,961,1,0,0,0,963,964,1,0,0,0,964,973,1,0,0,0,965,963,1,0, - 0,0,966,968,3,140,62,0,967,969,3,74,29,0,968,967,1,0,0,0,969,970,1,0,0, - 0,970,968,1,0,0,0,970,971,1,0,0,0,971,973,1,0,0,0,972,955,1,0,0,0,972,966, - 1,0,0,0,973,179,1,0,0,0,974,975,5,91,0,0,975,976,1,0,0,0,976,977,6,82,0, - 0,977,978,6,82,0,0,978,181,1,0,0,0,979,980,5,93,0,0,980,981,1,0,0,0,981, - 982,6,83,12,0,982,983,6,83,12,0,983,183,1,0,0,0,984,988,3,76,30,0,985,987, - 3,92,38,0,986,985,1,0,0,0,987,990,1,0,0,0,988,986,1,0,0,0,988,989,1,0,0, - 0,989,1001,1,0,0,0,990,988,1,0,0,0,991,994,3,90,37,0,992,994,3,84,34,0, - 993,991,1,0,0,0,993,992,1,0,0,0,994,996,1,0,0,0,995,997,3,92,38,0,996,995, - 1,0,0,0,997,998,1,0,0,0,998,996,1,0,0,0,998,999,1,0,0,0,999,1001,1,0,0, - 0,1000,984,1,0,0,0,1000,993,1,0,0,0,1001,185,1,0,0,0,1002,1004,3,86,35, - 0,1003,1005,3,88,36,0,1004,1003,1,0,0,0,1005,1006,1,0,0,0,1006,1004,1,0, - 0,0,1006,1007,1,0,0,0,1007,1008,1,0,0,0,1008,1009,3,86,35,0,1009,187,1, - 0,0,0,1010,1011,3,186,85,0,1011,189,1,0,0,0,1012,1013,3,66,25,0,1013,1014, - 1,0,0,0,1014,1015,6,87,11,0,1015,191,1,0,0,0,1016,1017,3,68,26,0,1017,1018, - 1,0,0,0,1018,1019,6,88,11,0,1019,193,1,0,0,0,1020,1021,3,70,27,0,1021,1022, - 1,0,0,0,1022,1023,6,89,11,0,1023,195,1,0,0,0,1024,1025,3,180,82,0,1025, - 1026,1,0,0,0,1026,1027,6,90,14,0,1027,1028,6,90,15,0,1028,197,1,0,0,0,1029, - 1030,3,72,28,0,1030,1031,1,0,0,0,1031,1032,6,91,16,0,1032,1033,6,91,12, - 0,1033,199,1,0,0,0,1034,1035,3,70,27,0,1035,1036,1,0,0,0,1036,1037,6,92, - 11,0,1037,201,1,0,0,0,1038,1039,3,66,25,0,1039,1040,1,0,0,0,1040,1041,6, - 93,11,0,1041,203,1,0,0,0,1042,1043,3,68,26,0,1043,1044,1,0,0,0,1044,1045, - 6,94,11,0,1045,205,1,0,0,0,1046,1047,3,72,28,0,1047,1048,1,0,0,0,1048,1049, - 6,95,16,0,1049,1050,6,95,12,0,1050,207,1,0,0,0,1051,1052,3,180,82,0,1052, - 1053,1,0,0,0,1053,1054,6,96,14,0,1054,209,1,0,0,0,1055,1056,3,182,83,0, - 1056,1057,1,0,0,0,1057,1058,6,97,17,0,1058,211,1,0,0,0,1059,1060,3,110, - 47,0,1060,1061,1,0,0,0,1061,1062,6,98,18,0,1062,213,1,0,0,0,1063,1064,3, - 112,48,0,1064,1065,1,0,0,0,1065,1066,6,99,19,0,1066,215,1,0,0,0,1067,1068, - 3,106,45,0,1068,1069,1,0,0,0,1069,1070,6,100,20,0,1070,217,1,0,0,0,1071, - 1072,7,16,0,0,1072,1073,7,3,0,0,1073,1074,7,5,0,0,1074,1075,7,12,0,0,1075, - 1076,7,0,0,0,1076,1077,7,12,0,0,1077,1078,7,5,0,0,1078,1079,7,12,0,0,1079, - 219,1,0,0,0,1080,1084,8,33,0,0,1081,1082,5,47,0,0,1082,1084,8,34,0,0,1083, - 1080,1,0,0,0,1083,1081,1,0,0,0,1084,221,1,0,0,0,1085,1087,3,220,102,0,1086, - 1085,1,0,0,0,1087,1088,1,0,0,0,1088,1086,1,0,0,0,1088,1089,1,0,0,0,1089, - 223,1,0,0,0,1090,1091,3,222,103,0,1091,1092,1,0,0,0,1092,1093,6,104,21, - 0,1093,225,1,0,0,0,1094,1095,3,94,39,0,1095,1096,1,0,0,0,1096,1097,6,105, - 22,0,1097,227,1,0,0,0,1098,1099,3,66,25,0,1099,1100,1,0,0,0,1100,1101,6, - 106,11,0,1101,229,1,0,0,0,1102,1103,3,68,26,0,1103,1104,1,0,0,0,1104,1105, - 6,107,11,0,1105,231,1,0,0,0,1106,1107,3,70,27,0,1107,1108,1,0,0,0,1108, - 1109,6,108,11,0,1109,233,1,0,0,0,1110,1111,3,72,28,0,1111,1112,1,0,0,0, - 1112,1113,6,109,16,0,1113,1114,6,109,12,0,1114,235,1,0,0,0,1115,1116,3, - 116,50,0,1116,1117,1,0,0,0,1117,1118,6,110,23,0,1118,237,1,0,0,0,1119,1120, - 3,112,48,0,1120,1121,1,0,0,0,1121,1122,6,111,19,0,1122,239,1,0,0,0,1123, - 1124,4,112,10,0,1124,1125,3,140,62,0,1125,1126,1,0,0,0,1126,1127,6,112, - 24,0,1127,241,1,0,0,0,1128,1129,4,113,11,0,1129,1130,3,178,81,0,1130,1131, - 1,0,0,0,1131,1132,6,113,25,0,1132,243,1,0,0,0,1133,1138,3,76,30,0,1134, - 1138,3,74,29,0,1135,1138,3,90,37,0,1136,1138,3,166,75,0,1137,1133,1,0,0, - 0,1137,1134,1,0,0,0,1137,1135,1,0,0,0,1137,1136,1,0,0,0,1138,245,1,0,0, - 0,1139,1142,3,76,30,0,1140,1142,3,166,75,0,1141,1139,1,0,0,0,1141,1140, - 1,0,0,0,1142,1146,1,0,0,0,1143,1145,3,244,114,0,1144,1143,1,0,0,0,1145, - 1148,1,0,0,0,1146,1144,1,0,0,0,1146,1147,1,0,0,0,1147,1159,1,0,0,0,1148, - 1146,1,0,0,0,1149,1152,3,90,37,0,1150,1152,3,84,34,0,1151,1149,1,0,0,0, - 1151,1150,1,0,0,0,1152,1154,1,0,0,0,1153,1155,3,244,114,0,1154,1153,1,0, - 0,0,1155,1156,1,0,0,0,1156,1154,1,0,0,0,1156,1157,1,0,0,0,1157,1159,1,0, - 0,0,1158,1141,1,0,0,0,1158,1151,1,0,0,0,1159,247,1,0,0,0,1160,1163,3,246, - 115,0,1161,1163,3,186,85,0,1162,1160,1,0,0,0,1162,1161,1,0,0,0,1163,1164, - 1,0,0,0,1164,1162,1,0,0,0,1164,1165,1,0,0,0,1165,249,1,0,0,0,1166,1167, - 3,66,25,0,1167,1168,1,0,0,0,1168,1169,6,117,11,0,1169,251,1,0,0,0,1170, - 1171,3,68,26,0,1171,1172,1,0,0,0,1172,1173,6,118,11,0,1173,253,1,0,0,0, - 1174,1175,3,70,27,0,1175,1176,1,0,0,0,1176,1177,6,119,11,0,1177,255,1,0, - 0,0,1178,1179,3,72,28,0,1179,1180,1,0,0,0,1180,1181,6,120,16,0,1181,1182, - 6,120,12,0,1182,257,1,0,0,0,1183,1184,3,106,45,0,1184,1185,1,0,0,0,1185, - 1186,6,121,20,0,1186,259,1,0,0,0,1187,1188,3,112,48,0,1188,1189,1,0,0,0, - 1189,1190,6,122,19,0,1190,261,1,0,0,0,1191,1192,3,116,50,0,1192,1193,1, - 0,0,0,1193,1194,6,123,23,0,1194,263,1,0,0,0,1195,1196,4,124,12,0,1196,1197, - 3,140,62,0,1197,1198,1,0,0,0,1198,1199,6,124,24,0,1199,265,1,0,0,0,1200, - 1201,4,125,13,0,1201,1202,3,178,81,0,1202,1203,1,0,0,0,1203,1204,6,125, - 25,0,1204,267,1,0,0,0,1205,1206,7,12,0,0,1206,1207,7,2,0,0,1207,269,1,0, - 0,0,1208,1209,3,248,116,0,1209,1210,1,0,0,0,1210,1211,6,127,26,0,1211,271, - 1,0,0,0,1212,1213,3,66,25,0,1213,1214,1,0,0,0,1214,1215,6,128,11,0,1215, - 273,1,0,0,0,1216,1217,3,68,26,0,1217,1218,1,0,0,0,1218,1219,6,129,11,0, - 1219,275,1,0,0,0,1220,1221,3,70,27,0,1221,1222,1,0,0,0,1222,1223,6,130, - 11,0,1223,277,1,0,0,0,1224,1225,3,72,28,0,1225,1226,1,0,0,0,1226,1227,6, - 131,16,0,1227,1228,6,131,12,0,1228,279,1,0,0,0,1229,1230,3,180,82,0,1230, - 1231,1,0,0,0,1231,1232,6,132,14,0,1232,1233,6,132,27,0,1233,281,1,0,0,0, - 1234,1235,7,7,0,0,1235,1236,7,9,0,0,1236,1237,1,0,0,0,1237,1238,6,133,28, - 0,1238,283,1,0,0,0,1239,1240,7,19,0,0,1240,1241,7,1,0,0,1241,1242,7,5,0, - 0,1242,1243,7,10,0,0,1243,1244,1,0,0,0,1244,1245,6,134,28,0,1245,285,1, - 0,0,0,1246,1247,8,35,0,0,1247,287,1,0,0,0,1248,1250,3,286,135,0,1249,1248, - 1,0,0,0,1250,1251,1,0,0,0,1251,1249,1,0,0,0,1251,1252,1,0,0,0,1252,1253, - 1,0,0,0,1253,1254,3,110,47,0,1254,1256,1,0,0,0,1255,1249,1,0,0,0,1255,1256, - 1,0,0,0,1256,1258,1,0,0,0,1257,1259,3,286,135,0,1258,1257,1,0,0,0,1259, - 1260,1,0,0,0,1260,1258,1,0,0,0,1260,1261,1,0,0,0,1261,289,1,0,0,0,1262, - 1263,3,288,136,0,1263,1264,1,0,0,0,1264,1265,6,137,29,0,1265,291,1,0,0, - 0,1266,1267,3,66,25,0,1267,1268,1,0,0,0,1268,1269,6,138,11,0,1269,293,1, - 0,0,0,1270,1271,3,68,26,0,1271,1272,1,0,0,0,1272,1273,6,139,11,0,1273,295, - 1,0,0,0,1274,1275,3,70,27,0,1275,1276,1,0,0,0,1276,1277,6,140,11,0,1277, - 297,1,0,0,0,1278,1279,3,72,28,0,1279,1280,1,0,0,0,1280,1281,6,141,16,0, - 1281,1282,6,141,12,0,1282,1283,6,141,12,0,1283,299,1,0,0,0,1284,1285,3, - 106,45,0,1285,1286,1,0,0,0,1286,1287,6,142,20,0,1287,301,1,0,0,0,1288,1289, - 3,112,48,0,1289,1290,1,0,0,0,1290,1291,6,143,19,0,1291,303,1,0,0,0,1292, - 1293,3,116,50,0,1293,1294,1,0,0,0,1294,1295,6,144,23,0,1295,305,1,0,0,0, - 1296,1297,3,284,134,0,1297,1298,1,0,0,0,1298,1299,6,145,30,0,1299,307,1, - 0,0,0,1300,1301,3,248,116,0,1301,1302,1,0,0,0,1302,1303,6,146,26,0,1303, - 309,1,0,0,0,1304,1305,3,188,86,0,1305,1306,1,0,0,0,1306,1307,6,147,31,0, - 1307,311,1,0,0,0,1308,1309,4,148,14,0,1309,1310,3,140,62,0,1310,1311,1, - 0,0,0,1311,1312,6,148,24,0,1312,313,1,0,0,0,1313,1314,4,149,15,0,1314,1315, - 3,178,81,0,1315,1316,1,0,0,0,1316,1317,6,149,25,0,1317,315,1,0,0,0,1318, - 1319,3,66,25,0,1319,1320,1,0,0,0,1320,1321,6,150,11,0,1321,317,1,0,0,0, - 1322,1323,3,68,26,0,1323,1324,1,0,0,0,1324,1325,6,151,11,0,1325,319,1,0, - 0,0,1326,1327,3,70,27,0,1327,1328,1,0,0,0,1328,1329,6,152,11,0,1329,321, - 1,0,0,0,1330,1331,3,72,28,0,1331,1332,1,0,0,0,1332,1333,6,153,16,0,1333, - 1334,6,153,12,0,1334,323,1,0,0,0,1335,1336,3,116,50,0,1336,1337,1,0,0,0, - 1337,1338,6,154,23,0,1338,325,1,0,0,0,1339,1340,4,155,16,0,1340,1341,3, - 140,62,0,1341,1342,1,0,0,0,1342,1343,6,155,24,0,1343,327,1,0,0,0,1344,1345, - 4,156,17,0,1345,1346,3,178,81,0,1346,1347,1,0,0,0,1347,1348,6,156,25,0, - 1348,329,1,0,0,0,1349,1350,3,188,86,0,1350,1351,1,0,0,0,1351,1352,6,157, - 31,0,1352,331,1,0,0,0,1353,1354,3,184,84,0,1354,1355,1,0,0,0,1355,1356, - 6,158,32,0,1356,333,1,0,0,0,1357,1358,3,66,25,0,1358,1359,1,0,0,0,1359, - 1360,6,159,11,0,1360,335,1,0,0,0,1361,1362,3,68,26,0,1362,1363,1,0,0,0, - 1363,1364,6,160,11,0,1364,337,1,0,0,0,1365,1366,3,70,27,0,1366,1367,1,0, - 0,0,1367,1368,6,161,11,0,1368,339,1,0,0,0,1369,1370,3,72,28,0,1370,1371, - 1,0,0,0,1371,1372,6,162,16,0,1372,1373,6,162,12,0,1373,341,1,0,0,0,1374, - 1375,7,1,0,0,1375,1376,7,9,0,0,1376,1377,7,15,0,0,1377,1378,7,7,0,0,1378, - 343,1,0,0,0,1379,1380,3,66,25,0,1380,1381,1,0,0,0,1381,1382,6,164,11,0, - 1382,345,1,0,0,0,1383,1384,3,68,26,0,1384,1385,1,0,0,0,1385,1386,6,165, - 11,0,1386,347,1,0,0,0,1387,1388,3,70,27,0,1388,1389,1,0,0,0,1389,1390,6, - 166,11,0,1390,349,1,0,0,0,1391,1392,3,182,83,0,1392,1393,1,0,0,0,1393,1394, - 6,167,17,0,1394,1395,6,167,12,0,1395,351,1,0,0,0,1396,1397,3,110,47,0,1397, - 1398,1,0,0,0,1398,1399,6,168,18,0,1399,353,1,0,0,0,1400,1406,3,84,34,0, - 1401,1406,3,74,29,0,1402,1406,3,116,50,0,1403,1406,3,76,30,0,1404,1406, - 3,90,37,0,1405,1400,1,0,0,0,1405,1401,1,0,0,0,1405,1402,1,0,0,0,1405,1403, - 1,0,0,0,1405,1404,1,0,0,0,1406,1407,1,0,0,0,1407,1405,1,0,0,0,1407,1408, - 1,0,0,0,1408,355,1,0,0,0,1409,1410,3,66,25,0,1410,1411,1,0,0,0,1411,1412, - 6,170,11,0,1412,357,1,0,0,0,1413,1414,3,68,26,0,1414,1415,1,0,0,0,1415, - 1416,6,171,11,0,1416,359,1,0,0,0,1417,1418,3,70,27,0,1418,1419,1,0,0,0, - 1419,1420,6,172,11,0,1420,361,1,0,0,0,1421,1422,3,72,28,0,1422,1423,1,0, - 0,0,1423,1424,6,173,16,0,1424,1425,6,173,12,0,1425,363,1,0,0,0,1426,1427, - 3,110,47,0,1427,1428,1,0,0,0,1428,1429,6,174,18,0,1429,365,1,0,0,0,1430, - 1431,3,112,48,0,1431,1432,1,0,0,0,1432,1433,6,175,19,0,1433,367,1,0,0,0, - 1434,1435,3,116,50,0,1435,1436,1,0,0,0,1436,1437,6,176,23,0,1437,369,1, - 0,0,0,1438,1439,3,282,133,0,1439,1440,1,0,0,0,1440,1441,6,177,33,0,1441, - 1442,6,177,34,0,1442,371,1,0,0,0,1443,1444,3,222,103,0,1444,1445,1,0,0, - 0,1445,1446,6,178,21,0,1446,373,1,0,0,0,1447,1448,3,94,39,0,1448,1449,1, - 0,0,0,1449,1450,6,179,22,0,1450,375,1,0,0,0,1451,1452,3,66,25,0,1452,1453, - 1,0,0,0,1453,1454,6,180,11,0,1454,377,1,0,0,0,1455,1456,3,68,26,0,1456, - 1457,1,0,0,0,1457,1458,6,181,11,0,1458,379,1,0,0,0,1459,1460,3,70,27,0, - 1460,1461,1,0,0,0,1461,1462,6,182,11,0,1462,381,1,0,0,0,1463,1464,3,72, - 28,0,1464,1465,1,0,0,0,1465,1466,6,183,16,0,1466,1467,6,183,12,0,1467,1468, - 6,183,12,0,1468,383,1,0,0,0,1469,1470,3,112,48,0,1470,1471,1,0,0,0,1471, - 1472,6,184,19,0,1472,385,1,0,0,0,1473,1474,3,116,50,0,1474,1475,1,0,0,0, - 1475,1476,6,185,23,0,1476,387,1,0,0,0,1477,1478,3,248,116,0,1478,1479,1, - 0,0,0,1479,1480,6,186,26,0,1480,389,1,0,0,0,1481,1482,3,66,25,0,1482,1483, - 1,0,0,0,1483,1484,6,187,11,0,1484,391,1,0,0,0,1485,1486,3,68,26,0,1486, - 1487,1,0,0,0,1487,1488,6,188,11,0,1488,393,1,0,0,0,1489,1490,3,70,27,0, - 1490,1491,1,0,0,0,1491,1492,6,189,11,0,1492,395,1,0,0,0,1493,1494,3,72, - 28,0,1494,1495,1,0,0,0,1495,1496,6,190,16,0,1496,1497,6,190,12,0,1497,397, - 1,0,0,0,1498,1499,3,54,19,0,1499,1500,1,0,0,0,1500,1501,6,191,35,0,1501, - 399,1,0,0,0,1502,1503,3,268,126,0,1503,1504,1,0,0,0,1504,1505,6,192,36, - 0,1505,401,1,0,0,0,1506,1507,3,282,133,0,1507,1508,1,0,0,0,1508,1509,6, - 193,33,0,1509,1510,6,193,12,0,1510,1511,6,193,0,0,1511,403,1,0,0,0,1512, - 1513,7,20,0,0,1513,1514,7,2,0,0,1514,1515,7,1,0,0,1515,1516,7,9,0,0,1516, - 1517,7,17,0,0,1517,1518,1,0,0,0,1518,1519,6,194,12,0,1519,1520,6,194,0, - 0,1520,405,1,0,0,0,1521,1522,3,222,103,0,1522,1523,1,0,0,0,1523,1524,6, - 195,21,0,1524,407,1,0,0,0,1525,1526,3,94,39,0,1526,1527,1,0,0,0,1527,1528, - 6,196,22,0,1528,409,1,0,0,0,1529,1530,3,110,47,0,1530,1531,1,0,0,0,1531, - 1532,6,197,18,0,1532,411,1,0,0,0,1533,1534,3,184,84,0,1534,1535,1,0,0,0, - 1535,1536,6,198,32,0,1536,413,1,0,0,0,1537,1538,3,188,86,0,1538,1539,1, - 0,0,0,1539,1540,6,199,31,0,1540,415,1,0,0,0,1541,1542,3,66,25,0,1542,1543, - 1,0,0,0,1543,1544,6,200,11,0,1544,417,1,0,0,0,1545,1546,3,68,26,0,1546, - 1547,1,0,0,0,1547,1548,6,201,11,0,1548,419,1,0,0,0,1549,1550,3,70,27,0, - 1550,1551,1,0,0,0,1551,1552,6,202,11,0,1552,421,1,0,0,0,1553,1554,3,72, - 28,0,1554,1555,1,0,0,0,1555,1556,6,203,16,0,1556,1557,6,203,12,0,1557,423, - 1,0,0,0,1558,1559,3,222,103,0,1559,1560,1,0,0,0,1560,1561,6,204,21,0,1561, - 1562,6,204,12,0,1562,1563,6,204,37,0,1563,425,1,0,0,0,1564,1565,3,94,39, - 0,1565,1566,1,0,0,0,1566,1567,6,205,22,0,1567,1568,6,205,12,0,1568,1569, - 6,205,37,0,1569,427,1,0,0,0,1570,1571,3,66,25,0,1571,1572,1,0,0,0,1572, - 1573,6,206,11,0,1573,429,1,0,0,0,1574,1575,3,68,26,0,1575,1576,1,0,0,0, - 1576,1577,6,207,11,0,1577,431,1,0,0,0,1578,1579,3,70,27,0,1579,1580,1,0, - 0,0,1580,1581,6,208,11,0,1581,433,1,0,0,0,1582,1583,3,110,47,0,1583,1584, - 1,0,0,0,1584,1585,6,209,18,0,1585,1586,6,209,12,0,1586,1587,6,209,9,0,1587, - 435,1,0,0,0,1588,1589,3,112,48,0,1589,1590,1,0,0,0,1590,1591,6,210,19,0, - 1591,1592,6,210,12,0,1592,1593,6,210,9,0,1593,437,1,0,0,0,1594,1595,3,66, - 25,0,1595,1596,1,0,0,0,1596,1597,6,211,11,0,1597,439,1,0,0,0,1598,1599, - 3,68,26,0,1599,1600,1,0,0,0,1600,1601,6,212,11,0,1601,441,1,0,0,0,1602, - 1603,3,70,27,0,1603,1604,1,0,0,0,1604,1605,6,213,11,0,1605,443,1,0,0,0, - 1606,1607,3,188,86,0,1607,1608,1,0,0,0,1608,1609,6,214,12,0,1609,1610,6, - 214,0,0,1610,1611,6,214,31,0,1611,445,1,0,0,0,1612,1613,3,184,84,0,1613, - 1614,1,0,0,0,1614,1615,6,215,12,0,1615,1616,6,215,0,0,1616,1617,6,215,32, - 0,1617,447,1,0,0,0,1618,1619,3,100,42,0,1619,1620,1,0,0,0,1620,1621,6,216, - 12,0,1621,1622,6,216,0,0,1622,1623,6,216,38,0,1623,449,1,0,0,0,1624,1625, - 3,72,28,0,1625,1626,1,0,0,0,1626,1627,6,217,16,0,1627,1628,6,217,12,0,1628, - 451,1,0,0,0,66,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,665,675,679,682,691, - 693,704,723,728,737,744,749,751,762,770,773,775,780,785,791,798,803,809, - 812,820,824,958,963,970,972,988,993,998,1000,1006,1083,1088,1137,1141,1146, - 1151,1156,1158,1162,1164,1251,1255,1260,1405,1407,39,5,1,0,5,4,0,5,6,0, - 5,2,0,5,3,0,5,8,0,5,5,0,5,9,0,5,11,0,5,14,0,5,13,0,0,1,0,4,0,0,7,16,0,7, - 72,0,5,0,0,7,29,0,7,73,0,7,38,0,7,39,0,7,36,0,7,83,0,7,30,0,7,41,0,7,53, - 0,7,71,0,7,87,0,5,10,0,5,7,0,7,97,0,7,96,0,7,75,0,7,74,0,7,95,0,5,12,0, - 7,20,0,7,91,0,5,15,0,7,33,0]; + 1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2, + 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,4,1,4,1,4, + 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,6,1,6,1,6,1,6, + 1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8, + 1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10, + 1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1, + 12,1,12,1,12,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,14,1,14,1,14,1,14,1,14, + 1,14,1,14,1,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,16,1,16,1,16,1, + 16,1,16,1,16,1,16,1,16,1,16,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17,1,17, + 1,17,1,17,1,17,1,17,1,17,1,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1, + 18,1,18,1,18,1,18,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19,1,19, + 1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,20,1,21,1,21,1,21,1,21,1,21,1,21,1, + 21,1,21,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,22,1,23,4,23,653,8,23, + 11,23,12,23,654,1,23,1,23,1,24,1,24,1,24,1,24,5,24,663,8,24,10,24,12,24, + 666,9,24,1,24,3,24,669,8,24,1,24,3,24,672,8,24,1,24,1,24,1,25,1,25,1,25, + 1,25,1,25,5,25,681,8,25,10,25,12,25,684,9,25,1,25,1,25,1,25,1,25,1,25,1, + 26,4,26,692,8,26,11,26,12,26,693,1,26,1,26,1,27,1,27,1,27,1,27,1,28,1,28, + 1,29,1,29,1,30,1,30,1,30,1,31,1,31,1,32,1,32,3,32,713,8,32,1,32,4,32,716, + 8,32,11,32,12,32,717,1,33,1,33,1,34,1,34,1,35,1,35,1,35,3,35,727,8,35,1, + 36,1,36,1,37,1,37,1,37,3,37,734,8,37,1,38,1,38,1,38,5,38,739,8,38,10,38, + 12,38,742,9,38,1,38,1,38,1,38,1,38,1,38,1,38,5,38,750,8,38,10,38,12,38, + 753,9,38,1,38,1,38,1,38,1,38,1,38,3,38,760,8,38,1,38,3,38,763,8,38,3,38, + 765,8,38,1,39,4,39,768,8,39,11,39,12,39,769,1,40,4,40,773,8,40,11,40,12, + 40,774,1,40,1,40,5,40,779,8,40,10,40,12,40,782,9,40,1,40,1,40,4,40,786, + 8,40,11,40,12,40,787,1,40,4,40,791,8,40,11,40,12,40,792,1,40,1,40,5,40, + 797,8,40,10,40,12,40,800,9,40,3,40,802,8,40,1,40,1,40,1,40,1,40,4,40,808, + 8,40,11,40,12,40,809,1,40,1,40,3,40,814,8,40,1,41,1,41,1,41,1,42,1,42,1, + 42,1,42,1,43,1,43,1,43,1,43,1,44,1,44,1,45,1,45,1,45,1,46,1,46,1,47,1,47, + 1,48,1,48,1,48,1,48,1,48,1,49,1,49,1,50,1,50,1,50,1,50,1,50,1,50,1,51,1, + 51,1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,53,1,53,1,53,1,54,1,54,1,54,1,54, + 1,54,1,55,1,55,1,55,1,55,1,55,1,56,1,56,1,57,1,57,1,57,1,57,1,58,1,58,1, + 58,1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,59,1,60,1,60,1,60,1,61,1,61,1,62, + 1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1, + 65,1,66,1,66,1,66,1,67,1,67,1,67,1,68,1,68,1,69,1,69,1,69,1,70,1,70,1,71, + 1,71,1,71,1,72,1,72,1,73,1,73,1,74,1,74,1,75,1,75,1,76,1,76,1,77,1,77,1, + 78,1,78,1,79,1,79,1,79,1,79,1,80,1,80,1,80,3,80,946,8,80,1,80,5,80,949, + 8,80,10,80,12,80,952,9,80,1,80,1,80,4,80,956,8,80,11,80,12,80,957,3,80, + 960,8,80,1,81,1,81,1,81,1,81,1,81,1,82,1,82,1,82,1,82,1,82,1,83,1,83,5, + 83,974,8,83,10,83,12,83,977,9,83,1,83,1,83,3,83,981,8,83,1,83,4,83,984, + 8,83,11,83,12,83,985,3,83,988,8,83,1,84,1,84,4,84,992,8,84,11,84,12,84, + 993,1,84,1,84,1,85,1,85,1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,88,1, + 88,1,88,1,88,1,89,1,89,1,89,1,89,1,89,1,90,1,90,1,90,1,90,1,90,1,91,1,91, + 1,91,1,91,1,92,1,92,1,92,1,92,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1, + 94,1,95,1,95,1,95,1,95,1,96,1,96,1,96,1,96,1,97,1,97,1,97,1,97,1,98,1,98, + 1,98,1,98,1,99,1,99,1,99,1,99,1,100,1,100,1,100,1,100,1,100,1,100,1,100, + 1,100,1,100,1,101,1,101,1,101,3,101,1071,8,101,1,102,4,102,1074,8,102,11, + 102,12,102,1075,1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,104,1,105,1, + 105,1,105,1,105,1,106,1,106,1,106,1,106,1,107,1,107,1,107,1,107,1,108,1, + 108,1,108,1,108,1,108,1,109,1,109,1,109,1,109,1,110,1,110,1,110,1,110,1, + 111,1,111,1,111,1,111,1,111,1,112,1,112,1,112,1,112,1,112,1,113,1,113,1, + 113,1,113,3,113,1125,8,113,1,114,1,114,3,114,1129,8,114,1,114,5,114,1132, + 8,114,10,114,12,114,1135,9,114,1,114,1,114,3,114,1139,8,114,1,114,4,114, + 1142,8,114,11,114,12,114,1143,3,114,1146,8,114,1,115,1,115,4,115,1150,8, + 115,11,115,12,115,1151,1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117, + 1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,119,1,119,1,120,1,120,1,120, + 1,120,1,121,1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,123,1,123,1,123, + 1,123,1,123,1,124,1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,126,1,126, + 1,126,1,126,1,127,1,127,1,127,1,127,1,128,1,128,1,128,1,128,1,129,1,129, + 1,129,1,129,1,130,1,130,1,130,1,130,1,130,1,131,1,131,1,131,1,131,1,131, + 1,132,1,132,1,132,1,132,1,132,1,133,1,133,1,133,1,133,1,133,1,133,1,133, + 1,134,1,134,1,135,4,135,1237,8,135,11,135,12,135,1238,1,135,1,135,3,135, + 1243,8,135,1,135,4,135,1246,8,135,11,135,12,135,1247,1,136,1,136,1,136, + 1,136,1,137,1,137,1,137,1,137,1,138,1,138,1,138,1,138,1,139,1,139,1,139, + 1,139,1,140,1,140,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,142, + 1,142,1,142,1,142,1,143,1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,145, + 1,145,1,145,1,145,1,146,1,146,1,146,1,146,1,147,1,147,1,147,1,147,1,147, + 1,148,1,148,1,148,1,148,1,148,1,149,1,149,1,149,1,149,1,150,1,150,1,150, + 1,150,1,151,1,151,1,151,1,151,1,152,1,152,1,152,1,152,1,152,1,153,1,153, + 1,153,1,153,1,154,1,154,1,154,1,154,1,154,1,155,1,155,1,155,1,155,1,155, + 1,156,1,156,1,156,1,156,1,157,1,157,1,157,1,157,1,158,1,158,1,158,1,158, + 1,159,1,159,1,159,1,159,1,160,1,160,1,160,1,160,1,161,1,161,1,161,1,161, + 1,161,1,162,1,162,1,162,1,162,1,162,1,163,1,163,1,163,1,163,1,164,1,164, + 1,164,1,164,1,165,1,165,1,165,1,165,1,166,1,166,1,166,1,166,1,166,1,167, + 1,167,1,167,1,167,1,168,1,168,1,168,1,168,1,168,4,168,1393,8,168,11,168, + 12,168,1394,1,169,1,169,1,169,1,169,1,170,1,170,1,170,1,170,1,171,1,171, + 1,171,1,171,1,172,1,172,1,172,1,172,1,172,1,173,1,173,1,173,1,173,1,174, + 1,174,1,174,1,174,1,175,1,175,1,175,1,175,1,176,1,176,1,176,1,176,1,176, + 1,177,1,177,1,177,1,177,1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179, + 1,180,1,180,1,180,1,180,1,181,1,181,1,181,1,181,1,182,1,182,1,182,1,182, + 1,182,1,182,1,183,1,183,1,183,1,183,1,184,1,184,1,184,1,184,1,185,1,185, + 1,185,1,185,1,186,1,186,1,186,1,186,1,187,1,187,1,187,1,187,1,188,1,188, + 1,188,1,188,1,189,1,189,1,189,1,189,1,189,1,190,1,190,1,190,1,190,1,190, + 1,191,1,191,1,191,1,191,1,192,1,192,1,192,1,192,1,192,1,192,1,193,1,193, + 1,193,1,193,1,193,1,193,1,193,1,193,1,193,1,194,1,194,1,194,1,194,1,195, + 1,195,1,195,1,195,1,196,1,196,1,196,1,196,1,197,1,197,1,197,1,197,1,198, + 1,198,1,198,1,198,1,199,1,199,1,199,1,199,1,200,1,200,1,200,1,200,1,201, + 1,201,1,201,1,201,1,202,1,202,1,202,1,202,1,202,1,203,1,203,1,203,1,203, + 1,203,1,203,1,204,1,204,1,204,1,204,1,204,1,204,1,205,1,205,1,205,1,205, + 1,206,1,206,1,206,1,206,1,207,1,207,1,207,1,207,1,208,1,208,1,208,1,208, + 1,208,1,208,1,209,1,209,1,209,1,209,1,209,1,209,1,210,1,210,1,210,1,210, + 1,211,1,211,1,211,1,211,1,212,1,212,1,212,1,212,1,213,1,213,1,213,1,213, + 1,213,1,213,1,214,1,214,1,214,1,214,1,214,1,214,1,215,1,215,1,215,1,215, + 1,215,1,215,1,216,1,216,1,216,1,216,1,216,2,682,751,0,217,16,1,18,2,20, + 3,22,4,24,5,26,6,28,7,30,8,32,9,34,10,36,11,38,12,40,13,42,14,44,15,46, + 16,48,17,50,18,52,19,54,20,56,21,58,22,60,23,62,24,64,25,66,26,68,27,70, + 28,72,0,74,0,76,0,78,0,80,0,82,0,84,0,86,0,88,0,90,0,92,29,94,30,96,31, + 98,32,100,33,102,34,104,35,106,36,108,37,110,38,112,39,114,40,116,41,118, + 42,120,43,122,44,124,45,126,46,128,47,130,48,132,49,134,50,136,51,138,52, + 140,53,142,54,144,55,146,56,148,57,150,58,152,59,154,60,156,61,158,62,160, + 63,162,64,164,65,166,66,168,67,170,68,172,69,174,0,176,70,178,71,180,72, + 182,73,184,0,186,74,188,75,190,76,192,77,194,0,196,0,198,78,200,79,202, + 80,204,0,206,0,208,0,210,0,212,0,214,0,216,81,218,0,220,82,222,0,224,0, + 226,83,228,84,230,85,232,0,234,0,236,0,238,0,240,0,242,0,244,0,246,86,248, + 87,250,88,252,89,254,0,256,0,258,0,260,0,262,0,264,0,266,90,268,0,270,91, + 272,92,274,93,276,0,278,0,280,94,282,95,284,0,286,96,288,0,290,97,292,98, + 294,99,296,0,298,0,300,0,302,0,304,0,306,0,308,0,310,0,312,0,314,100,316, + 101,318,102,320,0,322,0,324,0,326,0,328,0,330,0,332,103,334,104,336,105, + 338,0,340,106,342,107,344,108,346,109,348,0,350,0,352,110,354,111,356,112, + 358,113,360,0,362,0,364,0,366,0,368,0,370,0,372,0,374,114,376,115,378,116, + 380,0,382,0,384,0,386,0,388,117,390,118,392,119,394,0,396,120,398,0,400, + 0,402,121,404,0,406,0,408,0,410,0,412,0,414,122,416,123,418,124,420,0,422, + 0,424,0,426,125,428,126,430,127,432,0,434,0,436,128,438,129,440,130,442, + 0,444,0,446,0,448,0,16,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,36,2,0,68, + 68,100,100,2,0,73,73,105,105,2,0,83,83,115,115,2,0,69,69,101,101,2,0,67, + 67,99,99,2,0,84,84,116,116,2,0,82,82,114,114,2,0,79,79,111,111,2,0,80,80, + 112,112,2,0,78,78,110,110,2,0,72,72,104,104,2,0,86,86,118,118,2,0,65,65, + 97,97,2,0,76,76,108,108,2,0,88,88,120,120,2,0,70,70,102,102,2,0,77,77,109, + 109,2,0,71,71,103,103,2,0,75,75,107,107,2,0,87,87,119,119,2,0,85,85,117, + 117,6,0,9,10,13,13,32,32,47,47,91,91,93,93,2,0,10,10,13,13,3,0,9,10,13, + 13,32,32,1,0,48,57,2,0,65,90,97,122,8,0,34,34,78,78,82,82,84,84,92,92,110, + 110,114,114,116,116,4,0,10,10,13,13,34,34,92,92,2,0,43,43,45,45,1,0,96, + 96,2,0,66,66,98,98,2,0,89,89,121,121,11,0,9,10,13,13,32,32,34,34,44,44, + 47,47,58,58,61,61,91,91,93,93,124,124,2,0,42,42,47,47,11,0,9,10,13,13,32, + 32,34,35,44,44,47,47,58,58,60,60,62,63,92,92,124,124,2,0,74,74,106,106, + 1644,0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0,0,0,24,1,0,0,0,0, + 26,1,0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34,1,0,0,0,0,36,1,0, + 0,0,0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0,0,0,46,1,0,0,0,0, + 48,1,0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,0,56,1,0,0,0,0,58,1,0, + 0,0,0,60,1,0,0,0,0,62,1,0,0,0,0,64,1,0,0,0,0,66,1,0,0,0,0,68,1,0,0,0,1, + 70,1,0,0,0,1,92,1,0,0,0,1,94,1,0,0,0,1,96,1,0,0,0,1,98,1,0,0,0,1,100,1, + 0,0,0,1,102,1,0,0,0,1,104,1,0,0,0,1,106,1,0,0,0,1,108,1,0,0,0,1,110,1,0, + 0,0,1,112,1,0,0,0,1,114,1,0,0,0,1,116,1,0,0,0,1,118,1,0,0,0,1,120,1,0,0, + 0,1,122,1,0,0,0,1,124,1,0,0,0,1,126,1,0,0,0,1,128,1,0,0,0,1,130,1,0,0,0, + 1,132,1,0,0,0,1,134,1,0,0,0,1,136,1,0,0,0,1,138,1,0,0,0,1,140,1,0,0,0,1, + 142,1,0,0,0,1,144,1,0,0,0,1,146,1,0,0,0,1,148,1,0,0,0,1,150,1,0,0,0,1,152, + 1,0,0,0,1,154,1,0,0,0,1,156,1,0,0,0,1,158,1,0,0,0,1,160,1,0,0,0,1,162,1, + 0,0,0,1,164,1,0,0,0,1,166,1,0,0,0,1,168,1,0,0,0,1,170,1,0,0,0,1,172,1,0, + 0,0,1,174,1,0,0,0,1,176,1,0,0,0,1,178,1,0,0,0,1,180,1,0,0,0,1,182,1,0,0, + 0,1,186,1,0,0,0,1,188,1,0,0,0,1,190,1,0,0,0,1,192,1,0,0,0,2,194,1,0,0,0, + 2,196,1,0,0,0,2,198,1,0,0,0,2,200,1,0,0,0,2,202,1,0,0,0,3,204,1,0,0,0,3, + 206,1,0,0,0,3,208,1,0,0,0,3,210,1,0,0,0,3,212,1,0,0,0,3,214,1,0,0,0,3,216, + 1,0,0,0,3,220,1,0,0,0,3,222,1,0,0,0,3,224,1,0,0,0,3,226,1,0,0,0,3,228,1, + 0,0,0,3,230,1,0,0,0,4,232,1,0,0,0,4,234,1,0,0,0,4,236,1,0,0,0,4,238,1,0, + 0,0,4,240,1,0,0,0,4,246,1,0,0,0,4,248,1,0,0,0,4,250,1,0,0,0,4,252,1,0,0, + 0,5,254,1,0,0,0,5,256,1,0,0,0,5,258,1,0,0,0,5,260,1,0,0,0,5,262,1,0,0,0, + 5,264,1,0,0,0,5,266,1,0,0,0,5,268,1,0,0,0,5,270,1,0,0,0,5,272,1,0,0,0,5, + 274,1,0,0,0,6,276,1,0,0,0,6,278,1,0,0,0,6,280,1,0,0,0,6,282,1,0,0,0,6,286, + 1,0,0,0,6,288,1,0,0,0,6,290,1,0,0,0,6,292,1,0,0,0,6,294,1,0,0,0,7,296,1, + 0,0,0,7,298,1,0,0,0,7,300,1,0,0,0,7,302,1,0,0,0,7,304,1,0,0,0,7,306,1,0, + 0,0,7,308,1,0,0,0,7,310,1,0,0,0,7,312,1,0,0,0,7,314,1,0,0,0,7,316,1,0,0, + 0,7,318,1,0,0,0,8,320,1,0,0,0,8,322,1,0,0,0,8,324,1,0,0,0,8,326,1,0,0,0, + 8,328,1,0,0,0,8,330,1,0,0,0,8,332,1,0,0,0,8,334,1,0,0,0,8,336,1,0,0,0,9, + 338,1,0,0,0,9,340,1,0,0,0,9,342,1,0,0,0,9,344,1,0,0,0,9,346,1,0,0,0,10, + 348,1,0,0,0,10,350,1,0,0,0,10,352,1,0,0,0,10,354,1,0,0,0,10,356,1,0,0,0, + 10,358,1,0,0,0,11,360,1,0,0,0,11,362,1,0,0,0,11,364,1,0,0,0,11,366,1,0, + 0,0,11,368,1,0,0,0,11,370,1,0,0,0,11,372,1,0,0,0,11,374,1,0,0,0,11,376, + 1,0,0,0,11,378,1,0,0,0,12,380,1,0,0,0,12,382,1,0,0,0,12,384,1,0,0,0,12, + 386,1,0,0,0,12,388,1,0,0,0,12,390,1,0,0,0,12,392,1,0,0,0,13,394,1,0,0,0, + 13,396,1,0,0,0,13,398,1,0,0,0,13,400,1,0,0,0,13,402,1,0,0,0,13,404,1,0, + 0,0,13,406,1,0,0,0,13,408,1,0,0,0,13,410,1,0,0,0,13,412,1,0,0,0,13,414, + 1,0,0,0,13,416,1,0,0,0,13,418,1,0,0,0,14,420,1,0,0,0,14,422,1,0,0,0,14, + 424,1,0,0,0,14,426,1,0,0,0,14,428,1,0,0,0,14,430,1,0,0,0,15,432,1,0,0,0, + 15,434,1,0,0,0,15,436,1,0,0,0,15,438,1,0,0,0,15,440,1,0,0,0,15,442,1,0, + 0,0,15,444,1,0,0,0,15,446,1,0,0,0,15,448,1,0,0,0,16,450,1,0,0,0,18,460, + 1,0,0,0,20,467,1,0,0,0,22,476,1,0,0,0,24,483,1,0,0,0,26,493,1,0,0,0,28, + 500,1,0,0,0,30,507,1,0,0,0,32,514,1,0,0,0,34,522,1,0,0,0,36,534,1,0,0,0, + 38,543,1,0,0,0,40,549,1,0,0,0,42,556,1,0,0,0,44,563,1,0,0,0,46,571,1,0, + 0,0,48,579,1,0,0,0,50,588,1,0,0,0,52,603,1,0,0,0,54,615,1,0,0,0,56,626, + 1,0,0,0,58,634,1,0,0,0,60,642,1,0,0,0,62,652,1,0,0,0,64,658,1,0,0,0,66, + 675,1,0,0,0,68,691,1,0,0,0,70,697,1,0,0,0,72,701,1,0,0,0,74,703,1,0,0,0, + 76,705,1,0,0,0,78,708,1,0,0,0,80,710,1,0,0,0,82,719,1,0,0,0,84,721,1,0, + 0,0,86,726,1,0,0,0,88,728,1,0,0,0,90,733,1,0,0,0,92,764,1,0,0,0,94,767, + 1,0,0,0,96,813,1,0,0,0,98,815,1,0,0,0,100,818,1,0,0,0,102,822,1,0,0,0,104, + 826,1,0,0,0,106,828,1,0,0,0,108,831,1,0,0,0,110,833,1,0,0,0,112,835,1,0, + 0,0,114,840,1,0,0,0,116,842,1,0,0,0,118,848,1,0,0,0,120,854,1,0,0,0,122, + 857,1,0,0,0,124,860,1,0,0,0,126,865,1,0,0,0,128,870,1,0,0,0,130,872,1,0, + 0,0,132,876,1,0,0,0,134,881,1,0,0,0,136,887,1,0,0,0,138,890,1,0,0,0,140, + 892,1,0,0,0,142,898,1,0,0,0,144,900,1,0,0,0,146,905,1,0,0,0,148,908,1,0, + 0,0,150,911,1,0,0,0,152,914,1,0,0,0,154,916,1,0,0,0,156,919,1,0,0,0,158, + 921,1,0,0,0,160,924,1,0,0,0,162,926,1,0,0,0,164,928,1,0,0,0,166,930,1,0, + 0,0,168,932,1,0,0,0,170,934,1,0,0,0,172,936,1,0,0,0,174,938,1,0,0,0,176, + 959,1,0,0,0,178,961,1,0,0,0,180,966,1,0,0,0,182,987,1,0,0,0,184,989,1,0, + 0,0,186,997,1,0,0,0,188,999,1,0,0,0,190,1003,1,0,0,0,192,1007,1,0,0,0,194, + 1011,1,0,0,0,196,1016,1,0,0,0,198,1021,1,0,0,0,200,1025,1,0,0,0,202,1029, + 1,0,0,0,204,1033,1,0,0,0,206,1038,1,0,0,0,208,1042,1,0,0,0,210,1046,1,0, + 0,0,212,1050,1,0,0,0,214,1054,1,0,0,0,216,1058,1,0,0,0,218,1070,1,0,0,0, + 220,1073,1,0,0,0,222,1077,1,0,0,0,224,1081,1,0,0,0,226,1085,1,0,0,0,228, + 1089,1,0,0,0,230,1093,1,0,0,0,232,1097,1,0,0,0,234,1102,1,0,0,0,236,1106, + 1,0,0,0,238,1110,1,0,0,0,240,1115,1,0,0,0,242,1124,1,0,0,0,244,1145,1,0, + 0,0,246,1149,1,0,0,0,248,1153,1,0,0,0,250,1157,1,0,0,0,252,1161,1,0,0,0, + 254,1165,1,0,0,0,256,1170,1,0,0,0,258,1174,1,0,0,0,260,1178,1,0,0,0,262, + 1182,1,0,0,0,264,1187,1,0,0,0,266,1192,1,0,0,0,268,1195,1,0,0,0,270,1199, + 1,0,0,0,272,1203,1,0,0,0,274,1207,1,0,0,0,276,1211,1,0,0,0,278,1216,1,0, + 0,0,280,1221,1,0,0,0,282,1226,1,0,0,0,284,1233,1,0,0,0,286,1242,1,0,0,0, + 288,1249,1,0,0,0,290,1253,1,0,0,0,292,1257,1,0,0,0,294,1261,1,0,0,0,296, + 1265,1,0,0,0,298,1271,1,0,0,0,300,1275,1,0,0,0,302,1279,1,0,0,0,304,1283, + 1,0,0,0,306,1287,1,0,0,0,308,1291,1,0,0,0,310,1295,1,0,0,0,312,1300,1,0, + 0,0,314,1305,1,0,0,0,316,1309,1,0,0,0,318,1313,1,0,0,0,320,1317,1,0,0,0, + 322,1322,1,0,0,0,324,1326,1,0,0,0,326,1331,1,0,0,0,328,1336,1,0,0,0,330, + 1340,1,0,0,0,332,1344,1,0,0,0,334,1348,1,0,0,0,336,1352,1,0,0,0,338,1356, + 1,0,0,0,340,1361,1,0,0,0,342,1366,1,0,0,0,344,1370,1,0,0,0,346,1374,1,0, + 0,0,348,1378,1,0,0,0,350,1383,1,0,0,0,352,1392,1,0,0,0,354,1396,1,0,0,0, + 356,1400,1,0,0,0,358,1404,1,0,0,0,360,1408,1,0,0,0,362,1413,1,0,0,0,364, + 1417,1,0,0,0,366,1421,1,0,0,0,368,1425,1,0,0,0,370,1430,1,0,0,0,372,1434, + 1,0,0,0,374,1438,1,0,0,0,376,1442,1,0,0,0,378,1446,1,0,0,0,380,1450,1,0, + 0,0,382,1456,1,0,0,0,384,1460,1,0,0,0,386,1464,1,0,0,0,388,1468,1,0,0,0, + 390,1472,1,0,0,0,392,1476,1,0,0,0,394,1480,1,0,0,0,396,1485,1,0,0,0,398, + 1490,1,0,0,0,400,1494,1,0,0,0,402,1500,1,0,0,0,404,1509,1,0,0,0,406,1513, + 1,0,0,0,408,1517,1,0,0,0,410,1521,1,0,0,0,412,1525,1,0,0,0,414,1529,1,0, + 0,0,416,1533,1,0,0,0,418,1537,1,0,0,0,420,1541,1,0,0,0,422,1546,1,0,0,0, + 424,1552,1,0,0,0,426,1558,1,0,0,0,428,1562,1,0,0,0,430,1566,1,0,0,0,432, + 1570,1,0,0,0,434,1576,1,0,0,0,436,1582,1,0,0,0,438,1586,1,0,0,0,440,1590, + 1,0,0,0,442,1594,1,0,0,0,444,1600,1,0,0,0,446,1606,1,0,0,0,448,1612,1,0, + 0,0,450,451,7,0,0,0,451,452,7,1,0,0,452,453,7,2,0,0,453,454,7,2,0,0,454, + 455,7,3,0,0,455,456,7,4,0,0,456,457,7,5,0,0,457,458,1,0,0,0,458,459,6,0, + 0,0,459,17,1,0,0,0,460,461,7,0,0,0,461,462,7,6,0,0,462,463,7,7,0,0,463, + 464,7,8,0,0,464,465,1,0,0,0,465,466,6,1,1,0,466,19,1,0,0,0,467,468,7,3, + 0,0,468,469,7,9,0,0,469,470,7,6,0,0,470,471,7,1,0,0,471,472,7,4,0,0,472, + 473,7,10,0,0,473,474,1,0,0,0,474,475,6,2,2,0,475,21,1,0,0,0,476,477,7,3, + 0,0,477,478,7,11,0,0,478,479,7,12,0,0,479,480,7,13,0,0,480,481,1,0,0,0, + 481,482,6,3,0,0,482,23,1,0,0,0,483,484,7,3,0,0,484,485,7,14,0,0,485,486, + 7,8,0,0,486,487,7,13,0,0,487,488,7,12,0,0,488,489,7,1,0,0,489,490,7,9,0, + 0,490,491,1,0,0,0,491,492,6,4,3,0,492,25,1,0,0,0,493,494,7,15,0,0,494,495, + 7,6,0,0,495,496,7,7,0,0,496,497,7,16,0,0,497,498,1,0,0,0,498,499,6,5,4, + 0,499,27,1,0,0,0,500,501,7,17,0,0,501,502,7,6,0,0,502,503,7,7,0,0,503,504, + 7,18,0,0,504,505,1,0,0,0,505,506,6,6,0,0,506,29,1,0,0,0,507,508,7,18,0, + 0,508,509,7,3,0,0,509,510,7,3,0,0,510,511,7,8,0,0,511,512,1,0,0,0,512,513, + 6,7,1,0,513,31,1,0,0,0,514,515,7,13,0,0,515,516,7,1,0,0,516,517,7,16,0, + 0,517,518,7,1,0,0,518,519,7,5,0,0,519,520,1,0,0,0,520,521,6,8,0,0,521,33, + 1,0,0,0,522,523,7,16,0,0,523,524,7,11,0,0,524,525,5,95,0,0,525,526,7,3, + 0,0,526,527,7,14,0,0,527,528,7,8,0,0,528,529,7,12,0,0,529,530,7,9,0,0,530, + 531,7,0,0,0,531,532,1,0,0,0,532,533,6,9,5,0,533,35,1,0,0,0,534,535,7,6, + 0,0,535,536,7,3,0,0,536,537,7,9,0,0,537,538,7,12,0,0,538,539,7,16,0,0,539, + 540,7,3,0,0,540,541,1,0,0,0,541,542,6,10,6,0,542,37,1,0,0,0,543,544,7,6, + 0,0,544,545,7,7,0,0,545,546,7,19,0,0,546,547,1,0,0,0,547,548,6,11,0,0,548, + 39,1,0,0,0,549,550,7,2,0,0,550,551,7,10,0,0,551,552,7,7,0,0,552,553,7,19, + 0,0,553,554,1,0,0,0,554,555,6,12,7,0,555,41,1,0,0,0,556,557,7,2,0,0,557, + 558,7,7,0,0,558,559,7,6,0,0,559,560,7,5,0,0,560,561,1,0,0,0,561,562,6,13, + 0,0,562,43,1,0,0,0,563,564,7,2,0,0,564,565,7,5,0,0,565,566,7,12,0,0,566, + 567,7,5,0,0,567,568,7,2,0,0,568,569,1,0,0,0,569,570,6,14,0,0,570,45,1,0, + 0,0,571,572,7,19,0,0,572,573,7,10,0,0,573,574,7,3,0,0,574,575,7,6,0,0,575, + 576,7,3,0,0,576,577,1,0,0,0,577,578,6,15,0,0,578,47,1,0,0,0,579,580,7,13, + 0,0,580,581,7,7,0,0,581,582,7,7,0,0,582,583,7,18,0,0,583,584,7,20,0,0,584, + 585,7,8,0,0,585,586,1,0,0,0,586,587,6,16,8,0,587,49,1,0,0,0,588,589,4,17, + 0,0,589,590,7,1,0,0,590,591,7,9,0,0,591,592,7,13,0,0,592,593,7,1,0,0,593, + 594,7,9,0,0,594,595,7,3,0,0,595,596,7,2,0,0,596,597,7,5,0,0,597,598,7,12, + 0,0,598,599,7,5,0,0,599,600,7,2,0,0,600,601,1,0,0,0,601,602,6,17,0,0,602, + 51,1,0,0,0,603,604,4,18,1,0,604,605,7,13,0,0,605,606,7,7,0,0,606,607,7, + 7,0,0,607,608,7,18,0,0,608,609,7,20,0,0,609,610,7,8,0,0,610,611,5,95,0, + 0,611,612,5,128020,0,0,612,613,1,0,0,0,613,614,6,18,9,0,614,53,1,0,0,0, + 615,616,4,19,2,0,616,617,7,16,0,0,617,618,7,3,0,0,618,619,7,5,0,0,619,620, + 7,6,0,0,620,621,7,1,0,0,621,622,7,4,0,0,622,623,7,2,0,0,623,624,1,0,0,0, + 624,625,6,19,10,0,625,55,1,0,0,0,626,627,4,20,3,0,627,628,7,15,0,0,628, + 629,7,20,0,0,629,630,7,13,0,0,630,631,7,13,0,0,631,632,1,0,0,0,632,633, + 6,20,8,0,633,57,1,0,0,0,634,635,4,21,4,0,635,636,7,13,0,0,636,637,7,3,0, + 0,637,638,7,15,0,0,638,639,7,5,0,0,639,640,1,0,0,0,640,641,6,21,8,0,641, + 59,1,0,0,0,642,643,4,22,5,0,643,644,7,6,0,0,644,645,7,1,0,0,645,646,7,17, + 0,0,646,647,7,10,0,0,647,648,7,5,0,0,648,649,1,0,0,0,649,650,6,22,8,0,650, + 61,1,0,0,0,651,653,8,21,0,0,652,651,1,0,0,0,653,654,1,0,0,0,654,652,1,0, + 0,0,654,655,1,0,0,0,655,656,1,0,0,0,656,657,6,23,0,0,657,63,1,0,0,0,658, + 659,5,47,0,0,659,660,5,47,0,0,660,664,1,0,0,0,661,663,8,22,0,0,662,661, + 1,0,0,0,663,666,1,0,0,0,664,662,1,0,0,0,664,665,1,0,0,0,665,668,1,0,0,0, + 666,664,1,0,0,0,667,669,5,13,0,0,668,667,1,0,0,0,668,669,1,0,0,0,669,671, + 1,0,0,0,670,672,5,10,0,0,671,670,1,0,0,0,671,672,1,0,0,0,672,673,1,0,0, + 0,673,674,6,24,11,0,674,65,1,0,0,0,675,676,5,47,0,0,676,677,5,42,0,0,677, + 682,1,0,0,0,678,681,3,66,25,0,679,681,9,0,0,0,680,678,1,0,0,0,680,679,1, + 0,0,0,681,684,1,0,0,0,682,683,1,0,0,0,682,680,1,0,0,0,683,685,1,0,0,0,684, + 682,1,0,0,0,685,686,5,42,0,0,686,687,5,47,0,0,687,688,1,0,0,0,688,689,6, + 25,11,0,689,67,1,0,0,0,690,692,7,23,0,0,691,690,1,0,0,0,692,693,1,0,0,0, + 693,691,1,0,0,0,693,694,1,0,0,0,694,695,1,0,0,0,695,696,6,26,11,0,696,69, + 1,0,0,0,697,698,5,124,0,0,698,699,1,0,0,0,699,700,6,27,12,0,700,71,1,0, + 0,0,701,702,7,24,0,0,702,73,1,0,0,0,703,704,7,25,0,0,704,75,1,0,0,0,705, + 706,5,92,0,0,706,707,7,26,0,0,707,77,1,0,0,0,708,709,8,27,0,0,709,79,1, + 0,0,0,710,712,7,3,0,0,711,713,7,28,0,0,712,711,1,0,0,0,712,713,1,0,0,0, + 713,715,1,0,0,0,714,716,3,72,28,0,715,714,1,0,0,0,716,717,1,0,0,0,717,715, + 1,0,0,0,717,718,1,0,0,0,718,81,1,0,0,0,719,720,5,64,0,0,720,83,1,0,0,0, + 721,722,5,96,0,0,722,85,1,0,0,0,723,727,8,29,0,0,724,725,5,96,0,0,725,727, + 5,96,0,0,726,723,1,0,0,0,726,724,1,0,0,0,727,87,1,0,0,0,728,729,5,95,0, + 0,729,89,1,0,0,0,730,734,3,74,29,0,731,734,3,72,28,0,732,734,3,88,36,0, + 733,730,1,0,0,0,733,731,1,0,0,0,733,732,1,0,0,0,734,91,1,0,0,0,735,740, + 5,34,0,0,736,739,3,76,30,0,737,739,3,78,31,0,738,736,1,0,0,0,738,737,1, + 0,0,0,739,742,1,0,0,0,740,738,1,0,0,0,740,741,1,0,0,0,741,743,1,0,0,0,742, + 740,1,0,0,0,743,765,5,34,0,0,744,745,5,34,0,0,745,746,5,34,0,0,746,747, + 5,34,0,0,747,751,1,0,0,0,748,750,8,22,0,0,749,748,1,0,0,0,750,753,1,0,0, + 0,751,752,1,0,0,0,751,749,1,0,0,0,752,754,1,0,0,0,753,751,1,0,0,0,754,755, + 5,34,0,0,755,756,5,34,0,0,756,757,5,34,0,0,757,759,1,0,0,0,758,760,5,34, + 0,0,759,758,1,0,0,0,759,760,1,0,0,0,760,762,1,0,0,0,761,763,5,34,0,0,762, + 761,1,0,0,0,762,763,1,0,0,0,763,765,1,0,0,0,764,735,1,0,0,0,764,744,1,0, + 0,0,765,93,1,0,0,0,766,768,3,72,28,0,767,766,1,0,0,0,768,769,1,0,0,0,769, + 767,1,0,0,0,769,770,1,0,0,0,770,95,1,0,0,0,771,773,3,72,28,0,772,771,1, + 0,0,0,773,774,1,0,0,0,774,772,1,0,0,0,774,775,1,0,0,0,775,776,1,0,0,0,776, + 780,3,114,49,0,777,779,3,72,28,0,778,777,1,0,0,0,779,782,1,0,0,0,780,778, + 1,0,0,0,780,781,1,0,0,0,781,814,1,0,0,0,782,780,1,0,0,0,783,785,3,114,49, + 0,784,786,3,72,28,0,785,784,1,0,0,0,786,787,1,0,0,0,787,785,1,0,0,0,787, + 788,1,0,0,0,788,814,1,0,0,0,789,791,3,72,28,0,790,789,1,0,0,0,791,792,1, + 0,0,0,792,790,1,0,0,0,792,793,1,0,0,0,793,801,1,0,0,0,794,798,3,114,49, + 0,795,797,3,72,28,0,796,795,1,0,0,0,797,800,1,0,0,0,798,796,1,0,0,0,798, + 799,1,0,0,0,799,802,1,0,0,0,800,798,1,0,0,0,801,794,1,0,0,0,801,802,1,0, + 0,0,802,803,1,0,0,0,803,804,3,80,32,0,804,814,1,0,0,0,805,807,3,114,49, + 0,806,808,3,72,28,0,807,806,1,0,0,0,808,809,1,0,0,0,809,807,1,0,0,0,809, + 810,1,0,0,0,810,811,1,0,0,0,811,812,3,80,32,0,812,814,1,0,0,0,813,772,1, + 0,0,0,813,783,1,0,0,0,813,790,1,0,0,0,813,805,1,0,0,0,814,97,1,0,0,0,815, + 816,7,30,0,0,816,817,7,31,0,0,817,99,1,0,0,0,818,819,7,12,0,0,819,820,7, + 9,0,0,820,821,7,0,0,0,821,101,1,0,0,0,822,823,7,12,0,0,823,824,7,2,0,0, + 824,825,7,4,0,0,825,103,1,0,0,0,826,827,5,61,0,0,827,105,1,0,0,0,828,829, + 5,58,0,0,829,830,5,58,0,0,830,107,1,0,0,0,831,832,5,58,0,0,832,109,1,0, + 0,0,833,834,5,44,0,0,834,111,1,0,0,0,835,836,7,0,0,0,836,837,7,3,0,0,837, + 838,7,2,0,0,838,839,7,4,0,0,839,113,1,0,0,0,840,841,5,46,0,0,841,115,1, + 0,0,0,842,843,7,15,0,0,843,844,7,12,0,0,844,845,7,13,0,0,845,846,7,2,0, + 0,846,847,7,3,0,0,847,117,1,0,0,0,848,849,7,15,0,0,849,850,7,1,0,0,850, + 851,7,6,0,0,851,852,7,2,0,0,852,853,7,5,0,0,853,119,1,0,0,0,854,855,7,1, + 0,0,855,856,7,9,0,0,856,121,1,0,0,0,857,858,7,1,0,0,858,859,7,2,0,0,859, + 123,1,0,0,0,860,861,7,13,0,0,861,862,7,12,0,0,862,863,7,2,0,0,863,864,7, + 5,0,0,864,125,1,0,0,0,865,866,7,13,0,0,866,867,7,1,0,0,867,868,7,18,0,0, + 868,869,7,3,0,0,869,127,1,0,0,0,870,871,5,40,0,0,871,129,1,0,0,0,872,873, + 7,9,0,0,873,874,7,7,0,0,874,875,7,5,0,0,875,131,1,0,0,0,876,877,7,9,0,0, + 877,878,7,20,0,0,878,879,7,13,0,0,879,880,7,13,0,0,880,133,1,0,0,0,881, + 882,7,9,0,0,882,883,7,20,0,0,883,884,7,13,0,0,884,885,7,13,0,0,885,886, + 7,2,0,0,886,135,1,0,0,0,887,888,7,7,0,0,888,889,7,6,0,0,889,137,1,0,0,0, + 890,891,5,63,0,0,891,139,1,0,0,0,892,893,7,6,0,0,893,894,7,13,0,0,894,895, + 7,1,0,0,895,896,7,18,0,0,896,897,7,3,0,0,897,141,1,0,0,0,898,899,5,41,0, + 0,899,143,1,0,0,0,900,901,7,5,0,0,901,902,7,6,0,0,902,903,7,20,0,0,903, + 904,7,3,0,0,904,145,1,0,0,0,905,906,5,61,0,0,906,907,5,61,0,0,907,147,1, + 0,0,0,908,909,5,61,0,0,909,910,5,126,0,0,910,149,1,0,0,0,911,912,5,33,0, + 0,912,913,5,61,0,0,913,151,1,0,0,0,914,915,5,60,0,0,915,153,1,0,0,0,916, + 917,5,60,0,0,917,918,5,61,0,0,918,155,1,0,0,0,919,920,5,62,0,0,920,157, + 1,0,0,0,921,922,5,62,0,0,922,923,5,61,0,0,923,159,1,0,0,0,924,925,5,43, + 0,0,925,161,1,0,0,0,926,927,5,45,0,0,927,163,1,0,0,0,928,929,5,42,0,0,929, + 165,1,0,0,0,930,931,5,47,0,0,931,167,1,0,0,0,932,933,5,37,0,0,933,169,1, + 0,0,0,934,935,5,123,0,0,935,171,1,0,0,0,936,937,5,125,0,0,937,173,1,0,0, + 0,938,939,3,46,15,0,939,940,1,0,0,0,940,941,6,79,13,0,941,175,1,0,0,0,942, + 945,3,138,61,0,943,946,3,74,29,0,944,946,3,88,36,0,945,943,1,0,0,0,945, + 944,1,0,0,0,946,950,1,0,0,0,947,949,3,90,37,0,948,947,1,0,0,0,949,952,1, + 0,0,0,950,948,1,0,0,0,950,951,1,0,0,0,951,960,1,0,0,0,952,950,1,0,0,0,953, + 955,3,138,61,0,954,956,3,72,28,0,955,954,1,0,0,0,956,957,1,0,0,0,957,955, + 1,0,0,0,957,958,1,0,0,0,958,960,1,0,0,0,959,942,1,0,0,0,959,953,1,0,0,0, + 960,177,1,0,0,0,961,962,5,91,0,0,962,963,1,0,0,0,963,964,6,81,0,0,964,965, + 6,81,0,0,965,179,1,0,0,0,966,967,5,93,0,0,967,968,1,0,0,0,968,969,6,82, + 12,0,969,970,6,82,12,0,970,181,1,0,0,0,971,975,3,74,29,0,972,974,3,90,37, + 0,973,972,1,0,0,0,974,977,1,0,0,0,975,973,1,0,0,0,975,976,1,0,0,0,976,988, + 1,0,0,0,977,975,1,0,0,0,978,981,3,88,36,0,979,981,3,82,33,0,980,978,1,0, + 0,0,980,979,1,0,0,0,981,983,1,0,0,0,982,984,3,90,37,0,983,982,1,0,0,0,984, + 985,1,0,0,0,985,983,1,0,0,0,985,986,1,0,0,0,986,988,1,0,0,0,987,971,1,0, + 0,0,987,980,1,0,0,0,988,183,1,0,0,0,989,991,3,84,34,0,990,992,3,86,35,0, + 991,990,1,0,0,0,992,993,1,0,0,0,993,991,1,0,0,0,993,994,1,0,0,0,994,995, + 1,0,0,0,995,996,3,84,34,0,996,185,1,0,0,0,997,998,3,184,84,0,998,187,1, + 0,0,0,999,1000,3,64,24,0,1000,1001,1,0,0,0,1001,1002,6,86,11,0,1002,189, + 1,0,0,0,1003,1004,3,66,25,0,1004,1005,1,0,0,0,1005,1006,6,87,11,0,1006, + 191,1,0,0,0,1007,1008,3,68,26,0,1008,1009,1,0,0,0,1009,1010,6,88,11,0,1010, + 193,1,0,0,0,1011,1012,3,178,81,0,1012,1013,1,0,0,0,1013,1014,6,89,14,0, + 1014,1015,6,89,15,0,1015,195,1,0,0,0,1016,1017,3,70,27,0,1017,1018,1,0, + 0,0,1018,1019,6,90,16,0,1019,1020,6,90,12,0,1020,197,1,0,0,0,1021,1022, + 3,68,26,0,1022,1023,1,0,0,0,1023,1024,6,91,11,0,1024,199,1,0,0,0,1025,1026, + 3,64,24,0,1026,1027,1,0,0,0,1027,1028,6,92,11,0,1028,201,1,0,0,0,1029,1030, + 3,66,25,0,1030,1031,1,0,0,0,1031,1032,6,93,11,0,1032,203,1,0,0,0,1033,1034, + 3,70,27,0,1034,1035,1,0,0,0,1035,1036,6,94,16,0,1036,1037,6,94,12,0,1037, + 205,1,0,0,0,1038,1039,3,178,81,0,1039,1040,1,0,0,0,1040,1041,6,95,14,0, + 1041,207,1,0,0,0,1042,1043,3,180,82,0,1043,1044,1,0,0,0,1044,1045,6,96, + 17,0,1045,209,1,0,0,0,1046,1047,3,108,46,0,1047,1048,1,0,0,0,1048,1049, + 6,97,18,0,1049,211,1,0,0,0,1050,1051,3,110,47,0,1051,1052,1,0,0,0,1052, + 1053,6,98,19,0,1053,213,1,0,0,0,1054,1055,3,104,44,0,1055,1056,1,0,0,0, + 1056,1057,6,99,20,0,1057,215,1,0,0,0,1058,1059,7,16,0,0,1059,1060,7,3,0, + 0,1060,1061,7,5,0,0,1061,1062,7,12,0,0,1062,1063,7,0,0,0,1063,1064,7,12, + 0,0,1064,1065,7,5,0,0,1065,1066,7,12,0,0,1066,217,1,0,0,0,1067,1071,8,32, + 0,0,1068,1069,5,47,0,0,1069,1071,8,33,0,0,1070,1067,1,0,0,0,1070,1068,1, + 0,0,0,1071,219,1,0,0,0,1072,1074,3,218,101,0,1073,1072,1,0,0,0,1074,1075, + 1,0,0,0,1075,1073,1,0,0,0,1075,1076,1,0,0,0,1076,221,1,0,0,0,1077,1078, + 3,220,102,0,1078,1079,1,0,0,0,1079,1080,6,103,21,0,1080,223,1,0,0,0,1081, + 1082,3,92,38,0,1082,1083,1,0,0,0,1083,1084,6,104,22,0,1084,225,1,0,0,0, + 1085,1086,3,64,24,0,1086,1087,1,0,0,0,1087,1088,6,105,11,0,1088,227,1,0, + 0,0,1089,1090,3,66,25,0,1090,1091,1,0,0,0,1091,1092,6,106,11,0,1092,229, + 1,0,0,0,1093,1094,3,68,26,0,1094,1095,1,0,0,0,1095,1096,6,107,11,0,1096, + 231,1,0,0,0,1097,1098,3,70,27,0,1098,1099,1,0,0,0,1099,1100,6,108,16,0, + 1100,1101,6,108,12,0,1101,233,1,0,0,0,1102,1103,3,114,49,0,1103,1104,1, + 0,0,0,1104,1105,6,109,23,0,1105,235,1,0,0,0,1106,1107,3,110,47,0,1107,1108, + 1,0,0,0,1108,1109,6,110,19,0,1109,237,1,0,0,0,1110,1111,4,111,6,0,1111, + 1112,3,138,61,0,1112,1113,1,0,0,0,1113,1114,6,111,24,0,1114,239,1,0,0,0, + 1115,1116,4,112,7,0,1116,1117,3,176,80,0,1117,1118,1,0,0,0,1118,1119,6, + 112,25,0,1119,241,1,0,0,0,1120,1125,3,74,29,0,1121,1125,3,72,28,0,1122, + 1125,3,88,36,0,1123,1125,3,164,74,0,1124,1120,1,0,0,0,1124,1121,1,0,0,0, + 1124,1122,1,0,0,0,1124,1123,1,0,0,0,1125,243,1,0,0,0,1126,1129,3,74,29, + 0,1127,1129,3,164,74,0,1128,1126,1,0,0,0,1128,1127,1,0,0,0,1129,1133,1, + 0,0,0,1130,1132,3,242,113,0,1131,1130,1,0,0,0,1132,1135,1,0,0,0,1133,1131, + 1,0,0,0,1133,1134,1,0,0,0,1134,1146,1,0,0,0,1135,1133,1,0,0,0,1136,1139, + 3,88,36,0,1137,1139,3,82,33,0,1138,1136,1,0,0,0,1138,1137,1,0,0,0,1139, + 1141,1,0,0,0,1140,1142,3,242,113,0,1141,1140,1,0,0,0,1142,1143,1,0,0,0, + 1143,1141,1,0,0,0,1143,1144,1,0,0,0,1144,1146,1,0,0,0,1145,1128,1,0,0,0, + 1145,1138,1,0,0,0,1146,245,1,0,0,0,1147,1150,3,244,114,0,1148,1150,3,184, + 84,0,1149,1147,1,0,0,0,1149,1148,1,0,0,0,1150,1151,1,0,0,0,1151,1149,1, + 0,0,0,1151,1152,1,0,0,0,1152,247,1,0,0,0,1153,1154,3,64,24,0,1154,1155, + 1,0,0,0,1155,1156,6,116,11,0,1156,249,1,0,0,0,1157,1158,3,66,25,0,1158, + 1159,1,0,0,0,1159,1160,6,117,11,0,1160,251,1,0,0,0,1161,1162,3,68,26,0, + 1162,1163,1,0,0,0,1163,1164,6,118,11,0,1164,253,1,0,0,0,1165,1166,3,70, + 27,0,1166,1167,1,0,0,0,1167,1168,6,119,16,0,1168,1169,6,119,12,0,1169,255, + 1,0,0,0,1170,1171,3,104,44,0,1171,1172,1,0,0,0,1172,1173,6,120,20,0,1173, + 257,1,0,0,0,1174,1175,3,110,47,0,1175,1176,1,0,0,0,1176,1177,6,121,19,0, + 1177,259,1,0,0,0,1178,1179,3,114,49,0,1179,1180,1,0,0,0,1180,1181,6,122, + 23,0,1181,261,1,0,0,0,1182,1183,4,123,8,0,1183,1184,3,138,61,0,1184,1185, + 1,0,0,0,1185,1186,6,123,24,0,1186,263,1,0,0,0,1187,1188,4,124,9,0,1188, + 1189,3,176,80,0,1189,1190,1,0,0,0,1190,1191,6,124,25,0,1191,265,1,0,0,0, + 1192,1193,7,12,0,0,1193,1194,7,2,0,0,1194,267,1,0,0,0,1195,1196,3,246,115, + 0,1196,1197,1,0,0,0,1197,1198,6,126,26,0,1198,269,1,0,0,0,1199,1200,3,64, + 24,0,1200,1201,1,0,0,0,1201,1202,6,127,11,0,1202,271,1,0,0,0,1203,1204, + 3,66,25,0,1204,1205,1,0,0,0,1205,1206,6,128,11,0,1206,273,1,0,0,0,1207, + 1208,3,68,26,0,1208,1209,1,0,0,0,1209,1210,6,129,11,0,1210,275,1,0,0,0, + 1211,1212,3,70,27,0,1212,1213,1,0,0,0,1213,1214,6,130,16,0,1214,1215,6, + 130,12,0,1215,277,1,0,0,0,1216,1217,3,178,81,0,1217,1218,1,0,0,0,1218,1219, + 6,131,14,0,1219,1220,6,131,27,0,1220,279,1,0,0,0,1221,1222,7,7,0,0,1222, + 1223,7,9,0,0,1223,1224,1,0,0,0,1224,1225,6,132,28,0,1225,281,1,0,0,0,1226, + 1227,7,19,0,0,1227,1228,7,1,0,0,1228,1229,7,5,0,0,1229,1230,7,10,0,0,1230, + 1231,1,0,0,0,1231,1232,6,133,28,0,1232,283,1,0,0,0,1233,1234,8,34,0,0,1234, + 285,1,0,0,0,1235,1237,3,284,134,0,1236,1235,1,0,0,0,1237,1238,1,0,0,0,1238, + 1236,1,0,0,0,1238,1239,1,0,0,0,1239,1240,1,0,0,0,1240,1241,3,108,46,0,1241, + 1243,1,0,0,0,1242,1236,1,0,0,0,1242,1243,1,0,0,0,1243,1245,1,0,0,0,1244, + 1246,3,284,134,0,1245,1244,1,0,0,0,1246,1247,1,0,0,0,1247,1245,1,0,0,0, + 1247,1248,1,0,0,0,1248,287,1,0,0,0,1249,1250,3,286,135,0,1250,1251,1,0, + 0,0,1251,1252,6,136,29,0,1252,289,1,0,0,0,1253,1254,3,64,24,0,1254,1255, + 1,0,0,0,1255,1256,6,137,11,0,1256,291,1,0,0,0,1257,1258,3,66,25,0,1258, + 1259,1,0,0,0,1259,1260,6,138,11,0,1260,293,1,0,0,0,1261,1262,3,68,26,0, + 1262,1263,1,0,0,0,1263,1264,6,139,11,0,1264,295,1,0,0,0,1265,1266,3,70, + 27,0,1266,1267,1,0,0,0,1267,1268,6,140,16,0,1268,1269,6,140,12,0,1269,1270, + 6,140,12,0,1270,297,1,0,0,0,1271,1272,3,104,44,0,1272,1273,1,0,0,0,1273, + 1274,6,141,20,0,1274,299,1,0,0,0,1275,1276,3,110,47,0,1276,1277,1,0,0,0, + 1277,1278,6,142,19,0,1278,301,1,0,0,0,1279,1280,3,114,49,0,1280,1281,1, + 0,0,0,1281,1282,6,143,23,0,1282,303,1,0,0,0,1283,1284,3,282,133,0,1284, + 1285,1,0,0,0,1285,1286,6,144,30,0,1286,305,1,0,0,0,1287,1288,3,246,115, + 0,1288,1289,1,0,0,0,1289,1290,6,145,26,0,1290,307,1,0,0,0,1291,1292,3,186, + 85,0,1292,1293,1,0,0,0,1293,1294,6,146,31,0,1294,309,1,0,0,0,1295,1296, + 4,147,10,0,1296,1297,3,138,61,0,1297,1298,1,0,0,0,1298,1299,6,147,24,0, + 1299,311,1,0,0,0,1300,1301,4,148,11,0,1301,1302,3,176,80,0,1302,1303,1, + 0,0,0,1303,1304,6,148,25,0,1304,313,1,0,0,0,1305,1306,3,64,24,0,1306,1307, + 1,0,0,0,1307,1308,6,149,11,0,1308,315,1,0,0,0,1309,1310,3,66,25,0,1310, + 1311,1,0,0,0,1311,1312,6,150,11,0,1312,317,1,0,0,0,1313,1314,3,68,26,0, + 1314,1315,1,0,0,0,1315,1316,6,151,11,0,1316,319,1,0,0,0,1317,1318,3,70, + 27,0,1318,1319,1,0,0,0,1319,1320,6,152,16,0,1320,1321,6,152,12,0,1321,321, + 1,0,0,0,1322,1323,3,114,49,0,1323,1324,1,0,0,0,1324,1325,6,153,23,0,1325, + 323,1,0,0,0,1326,1327,4,154,12,0,1327,1328,3,138,61,0,1328,1329,1,0,0,0, + 1329,1330,6,154,24,0,1330,325,1,0,0,0,1331,1332,4,155,13,0,1332,1333,3, + 176,80,0,1333,1334,1,0,0,0,1334,1335,6,155,25,0,1335,327,1,0,0,0,1336,1337, + 3,186,85,0,1337,1338,1,0,0,0,1338,1339,6,156,31,0,1339,329,1,0,0,0,1340, + 1341,3,182,83,0,1341,1342,1,0,0,0,1342,1343,6,157,32,0,1343,331,1,0,0,0, + 1344,1345,3,64,24,0,1345,1346,1,0,0,0,1346,1347,6,158,11,0,1347,333,1,0, + 0,0,1348,1349,3,66,25,0,1349,1350,1,0,0,0,1350,1351,6,159,11,0,1351,335, + 1,0,0,0,1352,1353,3,68,26,0,1353,1354,1,0,0,0,1354,1355,6,160,11,0,1355, + 337,1,0,0,0,1356,1357,3,70,27,0,1357,1358,1,0,0,0,1358,1359,6,161,16,0, + 1359,1360,6,161,12,0,1360,339,1,0,0,0,1361,1362,7,1,0,0,1362,1363,7,9,0, + 0,1363,1364,7,15,0,0,1364,1365,7,7,0,0,1365,341,1,0,0,0,1366,1367,3,64, + 24,0,1367,1368,1,0,0,0,1368,1369,6,163,11,0,1369,343,1,0,0,0,1370,1371, + 3,66,25,0,1371,1372,1,0,0,0,1372,1373,6,164,11,0,1373,345,1,0,0,0,1374, + 1375,3,68,26,0,1375,1376,1,0,0,0,1376,1377,6,165,11,0,1377,347,1,0,0,0, + 1378,1379,3,180,82,0,1379,1380,1,0,0,0,1380,1381,6,166,17,0,1381,1382,6, + 166,12,0,1382,349,1,0,0,0,1383,1384,3,108,46,0,1384,1385,1,0,0,0,1385,1386, + 6,167,18,0,1386,351,1,0,0,0,1387,1393,3,82,33,0,1388,1393,3,72,28,0,1389, + 1393,3,114,49,0,1390,1393,3,74,29,0,1391,1393,3,88,36,0,1392,1387,1,0,0, + 0,1392,1388,1,0,0,0,1392,1389,1,0,0,0,1392,1390,1,0,0,0,1392,1391,1,0,0, + 0,1393,1394,1,0,0,0,1394,1392,1,0,0,0,1394,1395,1,0,0,0,1395,353,1,0,0, + 0,1396,1397,3,64,24,0,1397,1398,1,0,0,0,1398,1399,6,169,11,0,1399,355,1, + 0,0,0,1400,1401,3,66,25,0,1401,1402,1,0,0,0,1402,1403,6,170,11,0,1403,357, + 1,0,0,0,1404,1405,3,68,26,0,1405,1406,1,0,0,0,1406,1407,6,171,11,0,1407, + 359,1,0,0,0,1408,1409,3,70,27,0,1409,1410,1,0,0,0,1410,1411,6,172,16,0, + 1411,1412,6,172,12,0,1412,361,1,0,0,0,1413,1414,3,108,46,0,1414,1415,1, + 0,0,0,1415,1416,6,173,18,0,1416,363,1,0,0,0,1417,1418,3,110,47,0,1418,1419, + 1,0,0,0,1419,1420,6,174,19,0,1420,365,1,0,0,0,1421,1422,3,114,49,0,1422, + 1423,1,0,0,0,1423,1424,6,175,23,0,1424,367,1,0,0,0,1425,1426,3,280,132, + 0,1426,1427,1,0,0,0,1427,1428,6,176,33,0,1428,1429,6,176,34,0,1429,369, + 1,0,0,0,1430,1431,3,220,102,0,1431,1432,1,0,0,0,1432,1433,6,177,21,0,1433, + 371,1,0,0,0,1434,1435,3,92,38,0,1435,1436,1,0,0,0,1436,1437,6,178,22,0, + 1437,373,1,0,0,0,1438,1439,3,64,24,0,1439,1440,1,0,0,0,1440,1441,6,179, + 11,0,1441,375,1,0,0,0,1442,1443,3,66,25,0,1443,1444,1,0,0,0,1444,1445,6, + 180,11,0,1445,377,1,0,0,0,1446,1447,3,68,26,0,1447,1448,1,0,0,0,1448,1449, + 6,181,11,0,1449,379,1,0,0,0,1450,1451,3,70,27,0,1451,1452,1,0,0,0,1452, + 1453,6,182,16,0,1453,1454,6,182,12,0,1454,1455,6,182,12,0,1455,381,1,0, + 0,0,1456,1457,3,110,47,0,1457,1458,1,0,0,0,1458,1459,6,183,19,0,1459,383, + 1,0,0,0,1460,1461,3,114,49,0,1461,1462,1,0,0,0,1462,1463,6,184,23,0,1463, + 385,1,0,0,0,1464,1465,3,246,115,0,1465,1466,1,0,0,0,1466,1467,6,185,26, + 0,1467,387,1,0,0,0,1468,1469,3,64,24,0,1469,1470,1,0,0,0,1470,1471,6,186, + 11,0,1471,389,1,0,0,0,1472,1473,3,66,25,0,1473,1474,1,0,0,0,1474,1475,6, + 187,11,0,1475,391,1,0,0,0,1476,1477,3,68,26,0,1477,1478,1,0,0,0,1478,1479, + 6,188,11,0,1479,393,1,0,0,0,1480,1481,3,70,27,0,1481,1482,1,0,0,0,1482, + 1483,6,189,16,0,1483,1484,6,189,12,0,1484,395,1,0,0,0,1485,1486,7,35,0, + 0,1486,1487,7,7,0,0,1487,1488,7,1,0,0,1488,1489,7,9,0,0,1489,397,1,0,0, + 0,1490,1491,3,266,125,0,1491,1492,1,0,0,0,1492,1493,6,191,35,0,1493,399, + 1,0,0,0,1494,1495,3,280,132,0,1495,1496,1,0,0,0,1496,1497,6,192,33,0,1497, + 1498,6,192,12,0,1498,1499,6,192,0,0,1499,401,1,0,0,0,1500,1501,7,20,0,0, + 1501,1502,7,2,0,0,1502,1503,7,1,0,0,1503,1504,7,9,0,0,1504,1505,7,17,0, + 0,1505,1506,1,0,0,0,1506,1507,6,193,12,0,1507,1508,6,193,0,0,1508,403,1, + 0,0,0,1509,1510,3,220,102,0,1510,1511,1,0,0,0,1511,1512,6,194,21,0,1512, + 405,1,0,0,0,1513,1514,3,92,38,0,1514,1515,1,0,0,0,1515,1516,6,195,22,0, + 1516,407,1,0,0,0,1517,1518,3,108,46,0,1518,1519,1,0,0,0,1519,1520,6,196, + 18,0,1520,409,1,0,0,0,1521,1522,3,182,83,0,1522,1523,1,0,0,0,1523,1524, + 6,197,32,0,1524,411,1,0,0,0,1525,1526,3,186,85,0,1526,1527,1,0,0,0,1527, + 1528,6,198,31,0,1528,413,1,0,0,0,1529,1530,3,64,24,0,1530,1531,1,0,0,0, + 1531,1532,6,199,11,0,1532,415,1,0,0,0,1533,1534,3,66,25,0,1534,1535,1,0, + 0,0,1535,1536,6,200,11,0,1536,417,1,0,0,0,1537,1538,3,68,26,0,1538,1539, + 1,0,0,0,1539,1540,6,201,11,0,1540,419,1,0,0,0,1541,1542,3,70,27,0,1542, + 1543,1,0,0,0,1543,1544,6,202,16,0,1544,1545,6,202,12,0,1545,421,1,0,0,0, + 1546,1547,3,220,102,0,1547,1548,1,0,0,0,1548,1549,6,203,21,0,1549,1550, + 6,203,12,0,1550,1551,6,203,36,0,1551,423,1,0,0,0,1552,1553,3,92,38,0,1553, + 1554,1,0,0,0,1554,1555,6,204,22,0,1555,1556,6,204,12,0,1556,1557,6,204, + 36,0,1557,425,1,0,0,0,1558,1559,3,64,24,0,1559,1560,1,0,0,0,1560,1561,6, + 205,11,0,1561,427,1,0,0,0,1562,1563,3,66,25,0,1563,1564,1,0,0,0,1564,1565, + 6,206,11,0,1565,429,1,0,0,0,1566,1567,3,68,26,0,1567,1568,1,0,0,0,1568, + 1569,6,207,11,0,1569,431,1,0,0,0,1570,1571,3,108,46,0,1571,1572,1,0,0,0, + 1572,1573,6,208,18,0,1573,1574,6,208,12,0,1574,1575,6,208,10,0,1575,433, + 1,0,0,0,1576,1577,3,110,47,0,1577,1578,1,0,0,0,1578,1579,6,209,19,0,1579, + 1580,6,209,12,0,1580,1581,6,209,10,0,1581,435,1,0,0,0,1582,1583,3,64,24, + 0,1583,1584,1,0,0,0,1584,1585,6,210,11,0,1585,437,1,0,0,0,1586,1587,3,66, + 25,0,1587,1588,1,0,0,0,1588,1589,6,211,11,0,1589,439,1,0,0,0,1590,1591, + 3,68,26,0,1591,1592,1,0,0,0,1592,1593,6,212,11,0,1593,441,1,0,0,0,1594, + 1595,3,186,85,0,1595,1596,1,0,0,0,1596,1597,6,213,12,0,1597,1598,6,213, + 0,0,1598,1599,6,213,31,0,1599,443,1,0,0,0,1600,1601,3,182,83,0,1601,1602, + 1,0,0,0,1602,1603,6,214,12,0,1603,1604,6,214,0,0,1604,1605,6,214,32,0,1605, + 445,1,0,0,0,1606,1607,3,98,41,0,1607,1608,1,0,0,0,1608,1609,6,215,12,0, + 1609,1610,6,215,0,0,1610,1611,6,215,37,0,1611,447,1,0,0,0,1612,1613,3,70, + 27,0,1613,1614,1,0,0,0,1614,1615,6,216,16,0,1615,1616,6,216,12,0,1616,449, + 1,0,0,0,66,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,654,664,668,671,680,682, + 693,712,717,726,733,738,740,751,759,762,764,769,774,780,787,792,798,801, + 809,813,945,950,957,959,975,980,985,987,993,1070,1075,1124,1128,1133,1138, + 1143,1145,1149,1151,1238,1242,1247,1392,1394,38,5,1,0,5,4,0,5,6,0,5,2,0, + 5,3,0,5,8,0,5,5,0,5,9,0,5,13,0,5,11,0,5,14,0,0,1,0,4,0,0,7,16,0,7,71,0, + 5,0,0,7,28,0,7,72,0,7,37,0,7,38,0,7,35,0,7,82,0,7,29,0,7,40,0,7,52,0,7, + 70,0,7,86,0,5,10,0,5,7,0,7,96,0,7,95,0,7,74,0,7,73,0,7,94,0,5,12,0,7,90, + 0,5,15,0,7,32,0]; private static __ATN: ATN; public static get _ATN(): ATN { diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.g4 b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.g4 index 48e31bb6ff70f..bfd242a1a166c 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.g4 +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.g4 @@ -53,10 +53,10 @@ processingCommand | grokCommand | enrichCommand | mvExpandCommand + | joinCommand // in development | {this.isDevVersion()}? inlinestatsCommand | {this.isDevVersion()}? lookupCommand - | {this.isDevVersion()}? joinCommand ; whereCommand @@ -112,7 +112,7 @@ functionName ; mapExpression - : {this.isDevVersion()}? LEFT_BRACES entryExpression (COMMA entryExpression)* RIGHT_BRACES + : LEFT_BRACES entryExpression (COMMA entryExpression)* RIGHT_BRACES ; entryExpression @@ -326,11 +326,11 @@ inlinestatsCommand ; joinCommand - : type=(DEV_JOIN_LOOKUP | DEV_JOIN_LEFT | DEV_JOIN_RIGHT)? DEV_JOIN joinTarget joinCondition + : type=(JOIN_LOOKUP | DEV_JOIN_LEFT | DEV_JOIN_RIGHT) JOIN joinTarget joinCondition ; joinTarget - : index=indexPattern (AS alias=identifier)? + : index=indexPattern ; joinCondition diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.interp b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.interp index 9bed77ff31168..25a538f836472 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.interp +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.interp @@ -16,8 +16,7 @@ null 'sort' 'stats' 'where' -null -null +'lookup' null null null @@ -68,8 +67,8 @@ null '*' '/' '%' -null -null +'{' +'}' null null ']' @@ -120,6 +119,7 @@ null null null null +'join' 'USING' null null @@ -149,14 +149,13 @@ SHOW SORT STATS WHERE +JOIN_LOOKUP DEV_INLINESTATS DEV_LOOKUP DEV_METRICS -DEV_JOIN DEV_JOIN_FULL DEV_JOIN_LEFT DEV_JOIN_RIGHT -DEV_JOIN_LOOKUP UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT @@ -253,6 +252,7 @@ LOOKUP_WS LOOKUP_FIELD_LINE_COMMENT LOOKUP_FIELD_MULTILINE_COMMENT LOOKUP_FIELD_WS +JOIN USING JOIN_LINE_COMMENT JOIN_MULTILINE_COMMENT @@ -334,4 +334,4 @@ joinPredicate atn: -[4, 1, 130, 651, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 142, 8, 1, 10, 1, 12, 1, 145, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 153, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 173, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 185, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 192, 8, 5, 10, 5, 12, 5, 195, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 202, 8, 5, 1, 5, 1, 5, 1, 5, 3, 5, 207, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 215, 8, 5, 10, 5, 12, 5, 218, 9, 5, 1, 6, 1, 6, 3, 6, 222, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 229, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 234, 8, 6, 1, 7, 1, 7, 1, 7, 3, 7, 239, 8, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 249, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 255, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 263, 8, 9, 10, 9, 12, 9, 266, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 276, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 281, 8, 10, 10, 10, 12, 10, 284, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 292, 8, 11, 10, 11, 12, 11, 295, 9, 11, 1, 11, 1, 11, 3, 11, 299, 8, 11, 3, 11, 301, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 312, 8, 13, 10, 13, 12, 13, 315, 9, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 5, 17, 331, 8, 17, 10, 17, 12, 17, 334, 9, 17, 1, 18, 1, 18, 1, 18, 3, 18, 339, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 347, 8, 19, 10, 19, 12, 19, 350, 9, 19, 1, 19, 3, 19, 353, 8, 19, 1, 20, 1, 20, 1, 20, 3, 20, 358, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 370, 8, 23, 10, 23, 12, 23, 373, 9, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 379, 8, 24, 10, 24, 12, 24, 382, 9, 24, 1, 24, 3, 24, 385, 8, 24, 1, 24, 1, 24, 3, 24, 389, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 396, 8, 26, 1, 26, 1, 26, 3, 26, 400, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 405, 8, 27, 10, 27, 12, 27, 408, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 413, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 418, 8, 29, 10, 29, 12, 29, 421, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 426, 8, 30, 10, 30, 12, 30, 429, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 434, 8, 31, 10, 31, 12, 31, 437, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 444, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 459, 8, 34, 10, 34, 12, 34, 462, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 470, 8, 34, 10, 34, 12, 34, 473, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 481, 8, 34, 10, 34, 12, 34, 484, 9, 34, 1, 34, 1, 34, 3, 34, 488, 8, 34, 1, 35, 1, 35, 3, 35, 492, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 497, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 506, 8, 38, 10, 38, 12, 38, 509, 9, 38, 1, 39, 1, 39, 3, 39, 513, 8, 39, 1, 39, 1, 39, 3, 39, 517, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 529, 8, 42, 10, 42, 12, 42, 532, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 542, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 554, 8, 47, 10, 47, 12, 47, 557, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 567, 8, 50, 1, 51, 3, 51, 570, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 575, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 597, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 603, 8, 58, 10, 58, 12, 58, 606, 9, 58, 3, 58, 608, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 613, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 626, 8, 61, 1, 62, 3, 62, 629, 8, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 3, 63, 638, 8, 63, 1, 64, 1, 64, 1, 64, 1, 64, 5, 64, 644, 8, 64, 10, 64, 12, 64, 647, 9, 64, 1, 65, 1, 65, 1, 65, 0, 4, 2, 10, 18, 20, 66, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 0, 9, 1, 0, 64, 65, 1, 0, 66, 68, 2, 0, 30, 30, 83, 83, 1, 0, 74, 75, 2, 0, 35, 35, 40, 40, 2, 0, 43, 43, 46, 46, 2, 0, 42, 42, 56, 56, 2, 0, 57, 57, 59, 63, 1, 0, 22, 24, 678, 0, 132, 1, 0, 0, 0, 2, 135, 1, 0, 0, 0, 4, 152, 1, 0, 0, 0, 6, 172, 1, 0, 0, 0, 8, 174, 1, 0, 0, 0, 10, 206, 1, 0, 0, 0, 12, 233, 1, 0, 0, 0, 14, 235, 1, 0, 0, 0, 16, 248, 1, 0, 0, 0, 18, 254, 1, 0, 0, 0, 20, 275, 1, 0, 0, 0, 22, 285, 1, 0, 0, 0, 24, 304, 1, 0, 0, 0, 26, 306, 1, 0, 0, 0, 28, 318, 1, 0, 0, 0, 30, 322, 1, 0, 0, 0, 32, 324, 1, 0, 0, 0, 34, 327, 1, 0, 0, 0, 36, 338, 1, 0, 0, 0, 38, 342, 1, 0, 0, 0, 40, 357, 1, 0, 0, 0, 42, 361, 1, 0, 0, 0, 44, 363, 1, 0, 0, 0, 46, 365, 1, 0, 0, 0, 48, 374, 1, 0, 0, 0, 50, 390, 1, 0, 0, 0, 52, 393, 1, 0, 0, 0, 54, 401, 1, 0, 0, 0, 56, 409, 1, 0, 0, 0, 58, 414, 1, 0, 0, 0, 60, 422, 1, 0, 0, 0, 62, 430, 1, 0, 0, 0, 64, 438, 1, 0, 0, 0, 66, 443, 1, 0, 0, 0, 68, 487, 1, 0, 0, 0, 70, 491, 1, 0, 0, 0, 72, 496, 1, 0, 0, 0, 74, 498, 1, 0, 0, 0, 76, 501, 1, 0, 0, 0, 78, 510, 1, 0, 0, 0, 80, 518, 1, 0, 0, 0, 82, 521, 1, 0, 0, 0, 84, 524, 1, 0, 0, 0, 86, 533, 1, 0, 0, 0, 88, 537, 1, 0, 0, 0, 90, 543, 1, 0, 0, 0, 92, 547, 1, 0, 0, 0, 94, 550, 1, 0, 0, 0, 96, 558, 1, 0, 0, 0, 98, 562, 1, 0, 0, 0, 100, 566, 1, 0, 0, 0, 102, 569, 1, 0, 0, 0, 104, 574, 1, 0, 0, 0, 106, 578, 1, 0, 0, 0, 108, 580, 1, 0, 0, 0, 110, 582, 1, 0, 0, 0, 112, 585, 1, 0, 0, 0, 114, 589, 1, 0, 0, 0, 116, 592, 1, 0, 0, 0, 118, 612, 1, 0, 0, 0, 120, 616, 1, 0, 0, 0, 122, 621, 1, 0, 0, 0, 124, 628, 1, 0, 0, 0, 126, 634, 1, 0, 0, 0, 128, 639, 1, 0, 0, 0, 130, 648, 1, 0, 0, 0, 132, 133, 3, 2, 1, 0, 133, 134, 5, 0, 0, 1, 134, 1, 1, 0, 0, 0, 135, 136, 6, 1, -1, 0, 136, 137, 3, 4, 2, 0, 137, 143, 1, 0, 0, 0, 138, 139, 10, 1, 0, 0, 139, 140, 5, 29, 0, 0, 140, 142, 3, 6, 3, 0, 141, 138, 1, 0, 0, 0, 142, 145, 1, 0, 0, 0, 143, 141, 1, 0, 0, 0, 143, 144, 1, 0, 0, 0, 144, 3, 1, 0, 0, 0, 145, 143, 1, 0, 0, 0, 146, 153, 3, 110, 55, 0, 147, 153, 3, 38, 19, 0, 148, 153, 3, 32, 16, 0, 149, 153, 3, 114, 57, 0, 150, 151, 4, 2, 1, 0, 151, 153, 3, 48, 24, 0, 152, 146, 1, 0, 0, 0, 152, 147, 1, 0, 0, 0, 152, 148, 1, 0, 0, 0, 152, 149, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 153, 5, 1, 0, 0, 0, 154, 173, 3, 50, 25, 0, 155, 173, 3, 8, 4, 0, 156, 173, 3, 80, 40, 0, 157, 173, 3, 74, 37, 0, 158, 173, 3, 52, 26, 0, 159, 173, 3, 76, 38, 0, 160, 173, 3, 82, 41, 0, 161, 173, 3, 84, 42, 0, 162, 173, 3, 88, 44, 0, 163, 173, 3, 90, 45, 0, 164, 173, 3, 116, 58, 0, 165, 173, 3, 92, 46, 0, 166, 167, 4, 3, 2, 0, 167, 173, 3, 122, 61, 0, 168, 169, 4, 3, 3, 0, 169, 173, 3, 120, 60, 0, 170, 171, 4, 3, 4, 0, 171, 173, 3, 124, 62, 0, 172, 154, 1, 0, 0, 0, 172, 155, 1, 0, 0, 0, 172, 156, 1, 0, 0, 0, 172, 157, 1, 0, 0, 0, 172, 158, 1, 0, 0, 0, 172, 159, 1, 0, 0, 0, 172, 160, 1, 0, 0, 0, 172, 161, 1, 0, 0, 0, 172, 162, 1, 0, 0, 0, 172, 163, 1, 0, 0, 0, 172, 164, 1, 0, 0, 0, 172, 165, 1, 0, 0, 0, 172, 166, 1, 0, 0, 0, 172, 168, 1, 0, 0, 0, 172, 170, 1, 0, 0, 0, 173, 7, 1, 0, 0, 0, 174, 175, 5, 16, 0, 0, 175, 176, 3, 10, 5, 0, 176, 9, 1, 0, 0, 0, 177, 178, 6, 5, -1, 0, 178, 179, 5, 49, 0, 0, 179, 207, 3, 10, 5, 8, 180, 207, 3, 16, 8, 0, 181, 207, 3, 12, 6, 0, 182, 184, 3, 16, 8, 0, 183, 185, 5, 49, 0, 0, 184, 183, 1, 0, 0, 0, 184, 185, 1, 0, 0, 0, 185, 186, 1, 0, 0, 0, 186, 187, 5, 44, 0, 0, 187, 188, 5, 48, 0, 0, 188, 193, 3, 16, 8, 0, 189, 190, 5, 39, 0, 0, 190, 192, 3, 16, 8, 0, 191, 189, 1, 0, 0, 0, 192, 195, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 194, 1, 0, 0, 0, 194, 196, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 196, 197, 5, 55, 0, 0, 197, 207, 1, 0, 0, 0, 198, 199, 3, 16, 8, 0, 199, 201, 5, 45, 0, 0, 200, 202, 5, 49, 0, 0, 201, 200, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 1, 0, 0, 0, 203, 204, 5, 50, 0, 0, 204, 207, 1, 0, 0, 0, 205, 207, 3, 14, 7, 0, 206, 177, 1, 0, 0, 0, 206, 180, 1, 0, 0, 0, 206, 181, 1, 0, 0, 0, 206, 182, 1, 0, 0, 0, 206, 198, 1, 0, 0, 0, 206, 205, 1, 0, 0, 0, 207, 216, 1, 0, 0, 0, 208, 209, 10, 5, 0, 0, 209, 210, 5, 34, 0, 0, 210, 215, 3, 10, 5, 6, 211, 212, 10, 4, 0, 0, 212, 213, 5, 52, 0, 0, 213, 215, 3, 10, 5, 5, 214, 208, 1, 0, 0, 0, 214, 211, 1, 0, 0, 0, 215, 218, 1, 0, 0, 0, 216, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 11, 1, 0, 0, 0, 218, 216, 1, 0, 0, 0, 219, 221, 3, 16, 8, 0, 220, 222, 5, 49, 0, 0, 221, 220, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 5, 47, 0, 0, 224, 225, 3, 106, 53, 0, 225, 234, 1, 0, 0, 0, 226, 228, 3, 16, 8, 0, 227, 229, 5, 49, 0, 0, 228, 227, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 231, 5, 54, 0, 0, 231, 232, 3, 106, 53, 0, 232, 234, 1, 0, 0, 0, 233, 219, 1, 0, 0, 0, 233, 226, 1, 0, 0, 0, 234, 13, 1, 0, 0, 0, 235, 238, 3, 58, 29, 0, 236, 237, 5, 37, 0, 0, 237, 239, 3, 30, 15, 0, 238, 236, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 241, 5, 38, 0, 0, 241, 242, 3, 68, 34, 0, 242, 15, 1, 0, 0, 0, 243, 249, 3, 18, 9, 0, 244, 245, 3, 18, 9, 0, 245, 246, 3, 108, 54, 0, 246, 247, 3, 18, 9, 0, 247, 249, 1, 0, 0, 0, 248, 243, 1, 0, 0, 0, 248, 244, 1, 0, 0, 0, 249, 17, 1, 0, 0, 0, 250, 251, 6, 9, -1, 0, 251, 255, 3, 20, 10, 0, 252, 253, 7, 0, 0, 0, 253, 255, 3, 18, 9, 3, 254, 250, 1, 0, 0, 0, 254, 252, 1, 0, 0, 0, 255, 264, 1, 0, 0, 0, 256, 257, 10, 2, 0, 0, 257, 258, 7, 1, 0, 0, 258, 263, 3, 18, 9, 3, 259, 260, 10, 1, 0, 0, 260, 261, 7, 0, 0, 0, 261, 263, 3, 18, 9, 2, 262, 256, 1, 0, 0, 0, 262, 259, 1, 0, 0, 0, 263, 266, 1, 0, 0, 0, 264, 262, 1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 19, 1, 0, 0, 0, 266, 264, 1, 0, 0, 0, 267, 268, 6, 10, -1, 0, 268, 276, 3, 68, 34, 0, 269, 276, 3, 58, 29, 0, 270, 276, 3, 22, 11, 0, 271, 272, 5, 48, 0, 0, 272, 273, 3, 10, 5, 0, 273, 274, 5, 55, 0, 0, 274, 276, 1, 0, 0, 0, 275, 267, 1, 0, 0, 0, 275, 269, 1, 0, 0, 0, 275, 270, 1, 0, 0, 0, 275, 271, 1, 0, 0, 0, 276, 282, 1, 0, 0, 0, 277, 278, 10, 1, 0, 0, 278, 279, 5, 37, 0, 0, 279, 281, 3, 30, 15, 0, 280, 277, 1, 0, 0, 0, 281, 284, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 282, 283, 1, 0, 0, 0, 283, 21, 1, 0, 0, 0, 284, 282, 1, 0, 0, 0, 285, 286, 3, 24, 12, 0, 286, 300, 5, 48, 0, 0, 287, 301, 5, 66, 0, 0, 288, 293, 3, 10, 5, 0, 289, 290, 5, 39, 0, 0, 290, 292, 3, 10, 5, 0, 291, 289, 1, 0, 0, 0, 292, 295, 1, 0, 0, 0, 293, 291, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 298, 1, 0, 0, 0, 295, 293, 1, 0, 0, 0, 296, 297, 5, 39, 0, 0, 297, 299, 3, 26, 13, 0, 298, 296, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 301, 1, 0, 0, 0, 300, 287, 1, 0, 0, 0, 300, 288, 1, 0, 0, 0, 300, 301, 1, 0, 0, 0, 301, 302, 1, 0, 0, 0, 302, 303, 5, 55, 0, 0, 303, 23, 1, 0, 0, 0, 304, 305, 3, 72, 36, 0, 305, 25, 1, 0, 0, 0, 306, 307, 4, 13, 10, 0, 307, 308, 5, 69, 0, 0, 308, 313, 3, 28, 14, 0, 309, 310, 5, 39, 0, 0, 310, 312, 3, 28, 14, 0, 311, 309, 1, 0, 0, 0, 312, 315, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 313, 314, 1, 0, 0, 0, 314, 316, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 316, 317, 5, 70, 0, 0, 317, 27, 1, 0, 0, 0, 318, 319, 3, 106, 53, 0, 319, 320, 5, 38, 0, 0, 320, 321, 3, 68, 34, 0, 321, 29, 1, 0, 0, 0, 322, 323, 3, 64, 32, 0, 323, 31, 1, 0, 0, 0, 324, 325, 5, 12, 0, 0, 325, 326, 3, 34, 17, 0, 326, 33, 1, 0, 0, 0, 327, 332, 3, 36, 18, 0, 328, 329, 5, 39, 0, 0, 329, 331, 3, 36, 18, 0, 330, 328, 1, 0, 0, 0, 331, 334, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 35, 1, 0, 0, 0, 334, 332, 1, 0, 0, 0, 335, 336, 3, 58, 29, 0, 336, 337, 5, 36, 0, 0, 337, 339, 1, 0, 0, 0, 338, 335, 1, 0, 0, 0, 338, 339, 1, 0, 0, 0, 339, 340, 1, 0, 0, 0, 340, 341, 3, 10, 5, 0, 341, 37, 1, 0, 0, 0, 342, 343, 5, 6, 0, 0, 343, 348, 3, 40, 20, 0, 344, 345, 5, 39, 0, 0, 345, 347, 3, 40, 20, 0, 346, 344, 1, 0, 0, 0, 347, 350, 1, 0, 0, 0, 348, 346, 1, 0, 0, 0, 348, 349, 1, 0, 0, 0, 349, 352, 1, 0, 0, 0, 350, 348, 1, 0, 0, 0, 351, 353, 3, 46, 23, 0, 352, 351, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0, 353, 39, 1, 0, 0, 0, 354, 355, 3, 42, 21, 0, 355, 356, 5, 38, 0, 0, 356, 358, 1, 0, 0, 0, 357, 354, 1, 0, 0, 0, 357, 358, 1, 0, 0, 0, 358, 359, 1, 0, 0, 0, 359, 360, 3, 44, 22, 0, 360, 41, 1, 0, 0, 0, 361, 362, 5, 83, 0, 0, 362, 43, 1, 0, 0, 0, 363, 364, 7, 2, 0, 0, 364, 45, 1, 0, 0, 0, 365, 366, 5, 82, 0, 0, 366, 371, 5, 83, 0, 0, 367, 368, 5, 39, 0, 0, 368, 370, 5, 83, 0, 0, 369, 367, 1, 0, 0, 0, 370, 373, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0, 0, 0, 372, 47, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 374, 375, 5, 19, 0, 0, 375, 380, 3, 40, 20, 0, 376, 377, 5, 39, 0, 0, 377, 379, 3, 40, 20, 0, 378, 376, 1, 0, 0, 0, 379, 382, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 380, 381, 1, 0, 0, 0, 381, 384, 1, 0, 0, 0, 382, 380, 1, 0, 0, 0, 383, 385, 3, 54, 27, 0, 384, 383, 1, 0, 0, 0, 384, 385, 1, 0, 0, 0, 385, 388, 1, 0, 0, 0, 386, 387, 5, 33, 0, 0, 387, 389, 3, 34, 17, 0, 388, 386, 1, 0, 0, 0, 388, 389, 1, 0, 0, 0, 389, 49, 1, 0, 0, 0, 390, 391, 5, 4, 0, 0, 391, 392, 3, 34, 17, 0, 392, 51, 1, 0, 0, 0, 393, 395, 5, 15, 0, 0, 394, 396, 3, 54, 27, 0, 395, 394, 1, 0, 0, 0, 395, 396, 1, 0, 0, 0, 396, 399, 1, 0, 0, 0, 397, 398, 5, 33, 0, 0, 398, 400, 3, 34, 17, 0, 399, 397, 1, 0, 0, 0, 399, 400, 1, 0, 0, 0, 400, 53, 1, 0, 0, 0, 401, 406, 3, 56, 28, 0, 402, 403, 5, 39, 0, 0, 403, 405, 3, 56, 28, 0, 404, 402, 1, 0, 0, 0, 405, 408, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 406, 407, 1, 0, 0, 0, 407, 55, 1, 0, 0, 0, 408, 406, 1, 0, 0, 0, 409, 412, 3, 36, 18, 0, 410, 411, 5, 16, 0, 0, 411, 413, 3, 10, 5, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 57, 1, 0, 0, 0, 414, 419, 3, 72, 36, 0, 415, 416, 5, 41, 0, 0, 416, 418, 3, 72, 36, 0, 417, 415, 1, 0, 0, 0, 418, 421, 1, 0, 0, 0, 419, 417, 1, 0, 0, 0, 419, 420, 1, 0, 0, 0, 420, 59, 1, 0, 0, 0, 421, 419, 1, 0, 0, 0, 422, 427, 3, 66, 33, 0, 423, 424, 5, 41, 0, 0, 424, 426, 3, 66, 33, 0, 425, 423, 1, 0, 0, 0, 426, 429, 1, 0, 0, 0, 427, 425, 1, 0, 0, 0, 427, 428, 1, 0, 0, 0, 428, 61, 1, 0, 0, 0, 429, 427, 1, 0, 0, 0, 430, 435, 3, 60, 30, 0, 431, 432, 5, 39, 0, 0, 432, 434, 3, 60, 30, 0, 433, 431, 1, 0, 0, 0, 434, 437, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 435, 436, 1, 0, 0, 0, 436, 63, 1, 0, 0, 0, 437, 435, 1, 0, 0, 0, 438, 439, 7, 3, 0, 0, 439, 65, 1, 0, 0, 0, 440, 444, 5, 87, 0, 0, 441, 442, 4, 33, 11, 0, 442, 444, 3, 70, 35, 0, 443, 440, 1, 0, 0, 0, 443, 441, 1, 0, 0, 0, 444, 67, 1, 0, 0, 0, 445, 488, 5, 50, 0, 0, 446, 447, 3, 104, 52, 0, 447, 448, 5, 74, 0, 0, 448, 488, 1, 0, 0, 0, 449, 488, 3, 102, 51, 0, 450, 488, 3, 104, 52, 0, 451, 488, 3, 98, 49, 0, 452, 488, 3, 70, 35, 0, 453, 488, 3, 106, 53, 0, 454, 455, 5, 72, 0, 0, 455, 460, 3, 100, 50, 0, 456, 457, 5, 39, 0, 0, 457, 459, 3, 100, 50, 0, 458, 456, 1, 0, 0, 0, 459, 462, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 460, 461, 1, 0, 0, 0, 461, 463, 1, 0, 0, 0, 462, 460, 1, 0, 0, 0, 463, 464, 5, 73, 0, 0, 464, 488, 1, 0, 0, 0, 465, 466, 5, 72, 0, 0, 466, 471, 3, 98, 49, 0, 467, 468, 5, 39, 0, 0, 468, 470, 3, 98, 49, 0, 469, 467, 1, 0, 0, 0, 470, 473, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 474, 1, 0, 0, 0, 473, 471, 1, 0, 0, 0, 474, 475, 5, 73, 0, 0, 475, 488, 1, 0, 0, 0, 476, 477, 5, 72, 0, 0, 477, 482, 3, 106, 53, 0, 478, 479, 5, 39, 0, 0, 479, 481, 3, 106, 53, 0, 480, 478, 1, 0, 0, 0, 481, 484, 1, 0, 0, 0, 482, 480, 1, 0, 0, 0, 482, 483, 1, 0, 0, 0, 483, 485, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 485, 486, 5, 73, 0, 0, 486, 488, 1, 0, 0, 0, 487, 445, 1, 0, 0, 0, 487, 446, 1, 0, 0, 0, 487, 449, 1, 0, 0, 0, 487, 450, 1, 0, 0, 0, 487, 451, 1, 0, 0, 0, 487, 452, 1, 0, 0, 0, 487, 453, 1, 0, 0, 0, 487, 454, 1, 0, 0, 0, 487, 465, 1, 0, 0, 0, 487, 476, 1, 0, 0, 0, 488, 69, 1, 0, 0, 0, 489, 492, 5, 53, 0, 0, 490, 492, 5, 71, 0, 0, 491, 489, 1, 0, 0, 0, 491, 490, 1, 0, 0, 0, 492, 71, 1, 0, 0, 0, 493, 497, 3, 64, 32, 0, 494, 495, 4, 36, 12, 0, 495, 497, 3, 70, 35, 0, 496, 493, 1, 0, 0, 0, 496, 494, 1, 0, 0, 0, 497, 73, 1, 0, 0, 0, 498, 499, 5, 9, 0, 0, 499, 500, 5, 31, 0, 0, 500, 75, 1, 0, 0, 0, 501, 502, 5, 14, 0, 0, 502, 507, 3, 78, 39, 0, 503, 504, 5, 39, 0, 0, 504, 506, 3, 78, 39, 0, 505, 503, 1, 0, 0, 0, 506, 509, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 507, 508, 1, 0, 0, 0, 508, 77, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 512, 3, 10, 5, 0, 511, 513, 7, 4, 0, 0, 512, 511, 1, 0, 0, 0, 512, 513, 1, 0, 0, 0, 513, 516, 1, 0, 0, 0, 514, 515, 5, 51, 0, 0, 515, 517, 7, 5, 0, 0, 516, 514, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 79, 1, 0, 0, 0, 518, 519, 5, 8, 0, 0, 519, 520, 3, 62, 31, 0, 520, 81, 1, 0, 0, 0, 521, 522, 5, 2, 0, 0, 522, 523, 3, 62, 31, 0, 523, 83, 1, 0, 0, 0, 524, 525, 5, 11, 0, 0, 525, 530, 3, 86, 43, 0, 526, 527, 5, 39, 0, 0, 527, 529, 3, 86, 43, 0, 528, 526, 1, 0, 0, 0, 529, 532, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 85, 1, 0, 0, 0, 532, 530, 1, 0, 0, 0, 533, 534, 3, 60, 30, 0, 534, 535, 5, 91, 0, 0, 535, 536, 3, 60, 30, 0, 536, 87, 1, 0, 0, 0, 537, 538, 5, 1, 0, 0, 538, 539, 3, 20, 10, 0, 539, 541, 3, 106, 53, 0, 540, 542, 3, 94, 47, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 89, 1, 0, 0, 0, 543, 544, 5, 7, 0, 0, 544, 545, 3, 20, 10, 0, 545, 546, 3, 106, 53, 0, 546, 91, 1, 0, 0, 0, 547, 548, 5, 10, 0, 0, 548, 549, 3, 58, 29, 0, 549, 93, 1, 0, 0, 0, 550, 555, 3, 96, 48, 0, 551, 552, 5, 39, 0, 0, 552, 554, 3, 96, 48, 0, 553, 551, 1, 0, 0, 0, 554, 557, 1, 0, 0, 0, 555, 553, 1, 0, 0, 0, 555, 556, 1, 0, 0, 0, 556, 95, 1, 0, 0, 0, 557, 555, 1, 0, 0, 0, 558, 559, 3, 64, 32, 0, 559, 560, 5, 36, 0, 0, 560, 561, 3, 68, 34, 0, 561, 97, 1, 0, 0, 0, 562, 563, 7, 6, 0, 0, 563, 99, 1, 0, 0, 0, 564, 567, 3, 102, 51, 0, 565, 567, 3, 104, 52, 0, 566, 564, 1, 0, 0, 0, 566, 565, 1, 0, 0, 0, 567, 101, 1, 0, 0, 0, 568, 570, 7, 0, 0, 0, 569, 568, 1, 0, 0, 0, 569, 570, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 5, 32, 0, 0, 572, 103, 1, 0, 0, 0, 573, 575, 7, 0, 0, 0, 574, 573, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 576, 1, 0, 0, 0, 576, 577, 5, 31, 0, 0, 577, 105, 1, 0, 0, 0, 578, 579, 5, 30, 0, 0, 579, 107, 1, 0, 0, 0, 580, 581, 7, 7, 0, 0, 581, 109, 1, 0, 0, 0, 582, 583, 5, 5, 0, 0, 583, 584, 3, 112, 56, 0, 584, 111, 1, 0, 0, 0, 585, 586, 5, 72, 0, 0, 586, 587, 3, 2, 1, 0, 587, 588, 5, 73, 0, 0, 588, 113, 1, 0, 0, 0, 589, 590, 5, 13, 0, 0, 590, 591, 5, 107, 0, 0, 591, 115, 1, 0, 0, 0, 592, 593, 5, 3, 0, 0, 593, 596, 5, 97, 0, 0, 594, 595, 5, 95, 0, 0, 595, 597, 3, 60, 30, 0, 596, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 607, 1, 0, 0, 0, 598, 599, 5, 96, 0, 0, 599, 604, 3, 118, 59, 0, 600, 601, 5, 39, 0, 0, 601, 603, 3, 118, 59, 0, 602, 600, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 604, 605, 1, 0, 0, 0, 605, 608, 1, 0, 0, 0, 606, 604, 1, 0, 0, 0, 607, 598, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 117, 1, 0, 0, 0, 609, 610, 3, 60, 30, 0, 610, 611, 5, 36, 0, 0, 611, 613, 1, 0, 0, 0, 612, 609, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 615, 3, 60, 30, 0, 615, 119, 1, 0, 0, 0, 616, 617, 5, 18, 0, 0, 617, 618, 3, 40, 20, 0, 618, 619, 5, 95, 0, 0, 619, 620, 3, 62, 31, 0, 620, 121, 1, 0, 0, 0, 621, 622, 5, 17, 0, 0, 622, 625, 3, 54, 27, 0, 623, 624, 5, 33, 0, 0, 624, 626, 3, 34, 17, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 123, 1, 0, 0, 0, 627, 629, 7, 8, 0, 0, 628, 627, 1, 0, 0, 0, 628, 629, 1, 0, 0, 0, 629, 630, 1, 0, 0, 0, 630, 631, 5, 20, 0, 0, 631, 632, 3, 126, 63, 0, 632, 633, 3, 128, 64, 0, 633, 125, 1, 0, 0, 0, 634, 637, 3, 40, 20, 0, 635, 636, 5, 91, 0, 0, 636, 638, 3, 64, 32, 0, 637, 635, 1, 0, 0, 0, 637, 638, 1, 0, 0, 0, 638, 127, 1, 0, 0, 0, 639, 640, 5, 95, 0, 0, 640, 645, 3, 130, 65, 0, 641, 642, 5, 39, 0, 0, 642, 644, 3, 130, 65, 0, 643, 641, 1, 0, 0, 0, 644, 647, 1, 0, 0, 0, 645, 643, 1, 0, 0, 0, 645, 646, 1, 0, 0, 0, 646, 129, 1, 0, 0, 0, 647, 645, 1, 0, 0, 0, 648, 649, 3, 16, 8, 0, 649, 131, 1, 0, 0, 0, 63, 143, 152, 172, 184, 193, 201, 206, 214, 216, 221, 228, 233, 238, 248, 254, 262, 264, 275, 282, 293, 298, 300, 313, 332, 338, 348, 352, 357, 371, 380, 384, 388, 395, 399, 406, 412, 419, 427, 435, 443, 460, 471, 482, 487, 491, 496, 507, 512, 516, 530, 541, 555, 566, 569, 574, 596, 604, 607, 612, 625, 628, 637, 645] \ No newline at end of file +[4, 1, 130, 644, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 1, 142, 8, 1, 10, 1, 12, 1, 145, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 153, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 172, 8, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 184, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 191, 8, 5, 10, 5, 12, 5, 194, 9, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 201, 8, 5, 1, 5, 1, 5, 1, 5, 3, 5, 206, 8, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 5, 5, 214, 8, 5, 10, 5, 12, 5, 217, 9, 5, 1, 6, 1, 6, 3, 6, 221, 8, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 228, 8, 6, 1, 6, 1, 6, 1, 6, 3, 6, 233, 8, 6, 1, 7, 1, 7, 1, 7, 3, 7, 238, 8, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 248, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 254, 8, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 5, 9, 262, 8, 9, 10, 9, 12, 9, 265, 9, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 275, 8, 10, 1, 10, 1, 10, 1, 10, 5, 10, 280, 8, 10, 10, 10, 12, 10, 283, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 291, 8, 11, 10, 11, 12, 11, 294, 9, 11, 1, 11, 1, 11, 3, 11, 298, 8, 11, 3, 11, 300, 8, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 5, 13, 310, 8, 13, 10, 13, 12, 13, 313, 9, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 5, 17, 329, 8, 17, 10, 17, 12, 17, 332, 9, 17, 1, 18, 1, 18, 1, 18, 3, 18, 337, 8, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 5, 19, 345, 8, 19, 10, 19, 12, 19, 348, 9, 19, 1, 19, 3, 19, 351, 8, 19, 1, 20, 1, 20, 1, 20, 3, 20, 356, 8, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 23, 5, 23, 368, 8, 23, 10, 23, 12, 23, 371, 9, 23, 1, 24, 1, 24, 1, 24, 1, 24, 5, 24, 377, 8, 24, 10, 24, 12, 24, 380, 9, 24, 1, 24, 3, 24, 383, 8, 24, 1, 24, 1, 24, 3, 24, 387, 8, 24, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 3, 26, 394, 8, 26, 1, 26, 1, 26, 3, 26, 398, 8, 26, 1, 27, 1, 27, 1, 27, 5, 27, 403, 8, 27, 10, 27, 12, 27, 406, 9, 27, 1, 28, 1, 28, 1, 28, 3, 28, 411, 8, 28, 1, 29, 1, 29, 1, 29, 5, 29, 416, 8, 29, 10, 29, 12, 29, 419, 9, 29, 1, 30, 1, 30, 1, 30, 5, 30, 424, 8, 30, 10, 30, 12, 30, 427, 9, 30, 1, 31, 1, 31, 1, 31, 5, 31, 432, 8, 31, 10, 31, 12, 31, 435, 9, 31, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 3, 33, 442, 8, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 457, 8, 34, 10, 34, 12, 34, 460, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 468, 8, 34, 10, 34, 12, 34, 471, 9, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 479, 8, 34, 10, 34, 12, 34, 482, 9, 34, 1, 34, 1, 34, 3, 34, 486, 8, 34, 1, 35, 1, 35, 3, 35, 490, 8, 35, 1, 36, 1, 36, 1, 36, 3, 36, 495, 8, 36, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 5, 38, 504, 8, 38, 10, 38, 12, 38, 507, 9, 38, 1, 39, 1, 39, 3, 39, 511, 8, 39, 1, 39, 1, 39, 3, 39, 515, 8, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 5, 42, 527, 8, 42, 10, 42, 12, 42, 530, 9, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 540, 8, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 5, 47, 552, 8, 47, 10, 47, 12, 47, 555, 9, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 3, 50, 565, 8, 50, 1, 51, 3, 51, 568, 8, 51, 1, 51, 1, 51, 1, 52, 3, 52, 573, 8, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 3, 58, 595, 8, 58, 1, 58, 1, 58, 1, 58, 1, 58, 5, 58, 601, 8, 58, 10, 58, 12, 58, 604, 9, 58, 3, 58, 606, 8, 58, 1, 59, 1, 59, 1, 59, 3, 59, 611, 8, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 3, 61, 624, 8, 61, 1, 62, 1, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 64, 5, 64, 637, 8, 64, 10, 64, 12, 64, 640, 9, 64, 1, 65, 1, 65, 1, 65, 0, 4, 2, 10, 18, 20, 66, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 0, 9, 1, 0, 63, 64, 1, 0, 65, 67, 2, 0, 29, 29, 82, 82, 1, 0, 73, 74, 2, 0, 34, 34, 39, 39, 2, 0, 42, 42, 45, 45, 2, 0, 41, 41, 55, 55, 2, 0, 56, 56, 58, 62, 2, 0, 17, 17, 22, 23, 669, 0, 132, 1, 0, 0, 0, 2, 135, 1, 0, 0, 0, 4, 152, 1, 0, 0, 0, 6, 171, 1, 0, 0, 0, 8, 173, 1, 0, 0, 0, 10, 205, 1, 0, 0, 0, 12, 232, 1, 0, 0, 0, 14, 234, 1, 0, 0, 0, 16, 247, 1, 0, 0, 0, 18, 253, 1, 0, 0, 0, 20, 274, 1, 0, 0, 0, 22, 284, 1, 0, 0, 0, 24, 303, 1, 0, 0, 0, 26, 305, 1, 0, 0, 0, 28, 316, 1, 0, 0, 0, 30, 320, 1, 0, 0, 0, 32, 322, 1, 0, 0, 0, 34, 325, 1, 0, 0, 0, 36, 336, 1, 0, 0, 0, 38, 340, 1, 0, 0, 0, 40, 355, 1, 0, 0, 0, 42, 359, 1, 0, 0, 0, 44, 361, 1, 0, 0, 0, 46, 363, 1, 0, 0, 0, 48, 372, 1, 0, 0, 0, 50, 388, 1, 0, 0, 0, 52, 391, 1, 0, 0, 0, 54, 399, 1, 0, 0, 0, 56, 407, 1, 0, 0, 0, 58, 412, 1, 0, 0, 0, 60, 420, 1, 0, 0, 0, 62, 428, 1, 0, 0, 0, 64, 436, 1, 0, 0, 0, 66, 441, 1, 0, 0, 0, 68, 485, 1, 0, 0, 0, 70, 489, 1, 0, 0, 0, 72, 494, 1, 0, 0, 0, 74, 496, 1, 0, 0, 0, 76, 499, 1, 0, 0, 0, 78, 508, 1, 0, 0, 0, 80, 516, 1, 0, 0, 0, 82, 519, 1, 0, 0, 0, 84, 522, 1, 0, 0, 0, 86, 531, 1, 0, 0, 0, 88, 535, 1, 0, 0, 0, 90, 541, 1, 0, 0, 0, 92, 545, 1, 0, 0, 0, 94, 548, 1, 0, 0, 0, 96, 556, 1, 0, 0, 0, 98, 560, 1, 0, 0, 0, 100, 564, 1, 0, 0, 0, 102, 567, 1, 0, 0, 0, 104, 572, 1, 0, 0, 0, 106, 576, 1, 0, 0, 0, 108, 578, 1, 0, 0, 0, 110, 580, 1, 0, 0, 0, 112, 583, 1, 0, 0, 0, 114, 587, 1, 0, 0, 0, 116, 590, 1, 0, 0, 0, 118, 610, 1, 0, 0, 0, 120, 614, 1, 0, 0, 0, 122, 619, 1, 0, 0, 0, 124, 625, 1, 0, 0, 0, 126, 630, 1, 0, 0, 0, 128, 632, 1, 0, 0, 0, 130, 641, 1, 0, 0, 0, 132, 133, 3, 2, 1, 0, 133, 134, 5, 0, 0, 1, 134, 1, 1, 0, 0, 0, 135, 136, 6, 1, -1, 0, 136, 137, 3, 4, 2, 0, 137, 143, 1, 0, 0, 0, 138, 139, 10, 1, 0, 0, 139, 140, 5, 28, 0, 0, 140, 142, 3, 6, 3, 0, 141, 138, 1, 0, 0, 0, 142, 145, 1, 0, 0, 0, 143, 141, 1, 0, 0, 0, 143, 144, 1, 0, 0, 0, 144, 3, 1, 0, 0, 0, 145, 143, 1, 0, 0, 0, 146, 153, 3, 110, 55, 0, 147, 153, 3, 38, 19, 0, 148, 153, 3, 32, 16, 0, 149, 153, 3, 114, 57, 0, 150, 151, 4, 2, 1, 0, 151, 153, 3, 48, 24, 0, 152, 146, 1, 0, 0, 0, 152, 147, 1, 0, 0, 0, 152, 148, 1, 0, 0, 0, 152, 149, 1, 0, 0, 0, 152, 150, 1, 0, 0, 0, 153, 5, 1, 0, 0, 0, 154, 172, 3, 50, 25, 0, 155, 172, 3, 8, 4, 0, 156, 172, 3, 80, 40, 0, 157, 172, 3, 74, 37, 0, 158, 172, 3, 52, 26, 0, 159, 172, 3, 76, 38, 0, 160, 172, 3, 82, 41, 0, 161, 172, 3, 84, 42, 0, 162, 172, 3, 88, 44, 0, 163, 172, 3, 90, 45, 0, 164, 172, 3, 116, 58, 0, 165, 172, 3, 92, 46, 0, 166, 172, 3, 124, 62, 0, 167, 168, 4, 3, 2, 0, 168, 172, 3, 122, 61, 0, 169, 170, 4, 3, 3, 0, 170, 172, 3, 120, 60, 0, 171, 154, 1, 0, 0, 0, 171, 155, 1, 0, 0, 0, 171, 156, 1, 0, 0, 0, 171, 157, 1, 0, 0, 0, 171, 158, 1, 0, 0, 0, 171, 159, 1, 0, 0, 0, 171, 160, 1, 0, 0, 0, 171, 161, 1, 0, 0, 0, 171, 162, 1, 0, 0, 0, 171, 163, 1, 0, 0, 0, 171, 164, 1, 0, 0, 0, 171, 165, 1, 0, 0, 0, 171, 166, 1, 0, 0, 0, 171, 167, 1, 0, 0, 0, 171, 169, 1, 0, 0, 0, 172, 7, 1, 0, 0, 0, 173, 174, 5, 16, 0, 0, 174, 175, 3, 10, 5, 0, 175, 9, 1, 0, 0, 0, 176, 177, 6, 5, -1, 0, 177, 178, 5, 48, 0, 0, 178, 206, 3, 10, 5, 8, 179, 206, 3, 16, 8, 0, 180, 206, 3, 12, 6, 0, 181, 183, 3, 16, 8, 0, 182, 184, 5, 48, 0, 0, 183, 182, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184, 185, 1, 0, 0, 0, 185, 186, 5, 43, 0, 0, 186, 187, 5, 47, 0, 0, 187, 192, 3, 16, 8, 0, 188, 189, 5, 38, 0, 0, 189, 191, 3, 16, 8, 0, 190, 188, 1, 0, 0, 0, 191, 194, 1, 0, 0, 0, 192, 190, 1, 0, 0, 0, 192, 193, 1, 0, 0, 0, 193, 195, 1, 0, 0, 0, 194, 192, 1, 0, 0, 0, 195, 196, 5, 54, 0, 0, 196, 206, 1, 0, 0, 0, 197, 198, 3, 16, 8, 0, 198, 200, 5, 44, 0, 0, 199, 201, 5, 48, 0, 0, 200, 199, 1, 0, 0, 0, 200, 201, 1, 0, 0, 0, 201, 202, 1, 0, 0, 0, 202, 203, 5, 49, 0, 0, 203, 206, 1, 0, 0, 0, 204, 206, 3, 14, 7, 0, 205, 176, 1, 0, 0, 0, 205, 179, 1, 0, 0, 0, 205, 180, 1, 0, 0, 0, 205, 181, 1, 0, 0, 0, 205, 197, 1, 0, 0, 0, 205, 204, 1, 0, 0, 0, 206, 215, 1, 0, 0, 0, 207, 208, 10, 5, 0, 0, 208, 209, 5, 33, 0, 0, 209, 214, 3, 10, 5, 6, 210, 211, 10, 4, 0, 0, 211, 212, 5, 51, 0, 0, 212, 214, 3, 10, 5, 5, 213, 207, 1, 0, 0, 0, 213, 210, 1, 0, 0, 0, 214, 217, 1, 0, 0, 0, 215, 213, 1, 0, 0, 0, 215, 216, 1, 0, 0, 0, 216, 11, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0, 218, 220, 3, 16, 8, 0, 219, 221, 5, 48, 0, 0, 220, 219, 1, 0, 0, 0, 220, 221, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 223, 5, 46, 0, 0, 223, 224, 3, 106, 53, 0, 224, 233, 1, 0, 0, 0, 225, 227, 3, 16, 8, 0, 226, 228, 5, 48, 0, 0, 227, 226, 1, 0, 0, 0, 227, 228, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 230, 5, 53, 0, 0, 230, 231, 3, 106, 53, 0, 231, 233, 1, 0, 0, 0, 232, 218, 1, 0, 0, 0, 232, 225, 1, 0, 0, 0, 233, 13, 1, 0, 0, 0, 234, 237, 3, 58, 29, 0, 235, 236, 5, 36, 0, 0, 236, 238, 3, 30, 15, 0, 237, 235, 1, 0, 0, 0, 237, 238, 1, 0, 0, 0, 238, 239, 1, 0, 0, 0, 239, 240, 5, 37, 0, 0, 240, 241, 3, 68, 34, 0, 241, 15, 1, 0, 0, 0, 242, 248, 3, 18, 9, 0, 243, 244, 3, 18, 9, 0, 244, 245, 3, 108, 54, 0, 245, 246, 3, 18, 9, 0, 246, 248, 1, 0, 0, 0, 247, 242, 1, 0, 0, 0, 247, 243, 1, 0, 0, 0, 248, 17, 1, 0, 0, 0, 249, 250, 6, 9, -1, 0, 250, 254, 3, 20, 10, 0, 251, 252, 7, 0, 0, 0, 252, 254, 3, 18, 9, 3, 253, 249, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 254, 263, 1, 0, 0, 0, 255, 256, 10, 2, 0, 0, 256, 257, 7, 1, 0, 0, 257, 262, 3, 18, 9, 3, 258, 259, 10, 1, 0, 0, 259, 260, 7, 0, 0, 0, 260, 262, 3, 18, 9, 2, 261, 255, 1, 0, 0, 0, 261, 258, 1, 0, 0, 0, 262, 265, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 19, 1, 0, 0, 0, 265, 263, 1, 0, 0, 0, 266, 267, 6, 10, -1, 0, 267, 275, 3, 68, 34, 0, 268, 275, 3, 58, 29, 0, 269, 275, 3, 22, 11, 0, 270, 271, 5, 47, 0, 0, 271, 272, 3, 10, 5, 0, 272, 273, 5, 54, 0, 0, 273, 275, 1, 0, 0, 0, 274, 266, 1, 0, 0, 0, 274, 268, 1, 0, 0, 0, 274, 269, 1, 0, 0, 0, 274, 270, 1, 0, 0, 0, 275, 281, 1, 0, 0, 0, 276, 277, 10, 1, 0, 0, 277, 278, 5, 36, 0, 0, 278, 280, 3, 30, 15, 0, 279, 276, 1, 0, 0, 0, 280, 283, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0, 281, 282, 1, 0, 0, 0, 282, 21, 1, 0, 0, 0, 283, 281, 1, 0, 0, 0, 284, 285, 3, 24, 12, 0, 285, 299, 5, 47, 0, 0, 286, 300, 5, 65, 0, 0, 287, 292, 3, 10, 5, 0, 288, 289, 5, 38, 0, 0, 289, 291, 3, 10, 5, 0, 290, 288, 1, 0, 0, 0, 291, 294, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 292, 293, 1, 0, 0, 0, 293, 297, 1, 0, 0, 0, 294, 292, 1, 0, 0, 0, 295, 296, 5, 38, 0, 0, 296, 298, 3, 26, 13, 0, 297, 295, 1, 0, 0, 0, 297, 298, 1, 0, 0, 0, 298, 300, 1, 0, 0, 0, 299, 286, 1, 0, 0, 0, 299, 287, 1, 0, 0, 0, 299, 300, 1, 0, 0, 0, 300, 301, 1, 0, 0, 0, 301, 302, 5, 54, 0, 0, 302, 23, 1, 0, 0, 0, 303, 304, 3, 72, 36, 0, 304, 25, 1, 0, 0, 0, 305, 306, 5, 68, 0, 0, 306, 311, 3, 28, 14, 0, 307, 308, 5, 38, 0, 0, 308, 310, 3, 28, 14, 0, 309, 307, 1, 0, 0, 0, 310, 313, 1, 0, 0, 0, 311, 309, 1, 0, 0, 0, 311, 312, 1, 0, 0, 0, 312, 314, 1, 0, 0, 0, 313, 311, 1, 0, 0, 0, 314, 315, 5, 69, 0, 0, 315, 27, 1, 0, 0, 0, 316, 317, 3, 106, 53, 0, 317, 318, 5, 37, 0, 0, 318, 319, 3, 68, 34, 0, 319, 29, 1, 0, 0, 0, 320, 321, 3, 64, 32, 0, 321, 31, 1, 0, 0, 0, 322, 323, 5, 12, 0, 0, 323, 324, 3, 34, 17, 0, 324, 33, 1, 0, 0, 0, 325, 330, 3, 36, 18, 0, 326, 327, 5, 38, 0, 0, 327, 329, 3, 36, 18, 0, 328, 326, 1, 0, 0, 0, 329, 332, 1, 0, 0, 0, 330, 328, 1, 0, 0, 0, 330, 331, 1, 0, 0, 0, 331, 35, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 333, 334, 3, 58, 29, 0, 334, 335, 5, 35, 0, 0, 335, 337, 1, 0, 0, 0, 336, 333, 1, 0, 0, 0, 336, 337, 1, 0, 0, 0, 337, 338, 1, 0, 0, 0, 338, 339, 3, 10, 5, 0, 339, 37, 1, 0, 0, 0, 340, 341, 5, 6, 0, 0, 341, 346, 3, 40, 20, 0, 342, 343, 5, 38, 0, 0, 343, 345, 3, 40, 20, 0, 344, 342, 1, 0, 0, 0, 345, 348, 1, 0, 0, 0, 346, 344, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 350, 1, 0, 0, 0, 348, 346, 1, 0, 0, 0, 349, 351, 3, 46, 23, 0, 350, 349, 1, 0, 0, 0, 350, 351, 1, 0, 0, 0, 351, 39, 1, 0, 0, 0, 352, 353, 3, 42, 21, 0, 353, 354, 5, 37, 0, 0, 354, 356, 1, 0, 0, 0, 355, 352, 1, 0, 0, 0, 355, 356, 1, 0, 0, 0, 356, 357, 1, 0, 0, 0, 357, 358, 3, 44, 22, 0, 358, 41, 1, 0, 0, 0, 359, 360, 5, 82, 0, 0, 360, 43, 1, 0, 0, 0, 361, 362, 7, 2, 0, 0, 362, 45, 1, 0, 0, 0, 363, 364, 5, 81, 0, 0, 364, 369, 5, 82, 0, 0, 365, 366, 5, 38, 0, 0, 366, 368, 5, 82, 0, 0, 367, 365, 1, 0, 0, 0, 368, 371, 1, 0, 0, 0, 369, 367, 1, 0, 0, 0, 369, 370, 1, 0, 0, 0, 370, 47, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 372, 373, 5, 20, 0, 0, 373, 378, 3, 40, 20, 0, 374, 375, 5, 38, 0, 0, 375, 377, 3, 40, 20, 0, 376, 374, 1, 0, 0, 0, 377, 380, 1, 0, 0, 0, 378, 376, 1, 0, 0, 0, 378, 379, 1, 0, 0, 0, 379, 382, 1, 0, 0, 0, 380, 378, 1, 0, 0, 0, 381, 383, 3, 54, 27, 0, 382, 381, 1, 0, 0, 0, 382, 383, 1, 0, 0, 0, 383, 386, 1, 0, 0, 0, 384, 385, 5, 32, 0, 0, 385, 387, 3, 34, 17, 0, 386, 384, 1, 0, 0, 0, 386, 387, 1, 0, 0, 0, 387, 49, 1, 0, 0, 0, 388, 389, 5, 4, 0, 0, 389, 390, 3, 34, 17, 0, 390, 51, 1, 0, 0, 0, 391, 393, 5, 15, 0, 0, 392, 394, 3, 54, 27, 0, 393, 392, 1, 0, 0, 0, 393, 394, 1, 0, 0, 0, 394, 397, 1, 0, 0, 0, 395, 396, 5, 32, 0, 0, 396, 398, 3, 34, 17, 0, 397, 395, 1, 0, 0, 0, 397, 398, 1, 0, 0, 0, 398, 53, 1, 0, 0, 0, 399, 404, 3, 56, 28, 0, 400, 401, 5, 38, 0, 0, 401, 403, 3, 56, 28, 0, 402, 400, 1, 0, 0, 0, 403, 406, 1, 0, 0, 0, 404, 402, 1, 0, 0, 0, 404, 405, 1, 0, 0, 0, 405, 55, 1, 0, 0, 0, 406, 404, 1, 0, 0, 0, 407, 410, 3, 36, 18, 0, 408, 409, 5, 16, 0, 0, 409, 411, 3, 10, 5, 0, 410, 408, 1, 0, 0, 0, 410, 411, 1, 0, 0, 0, 411, 57, 1, 0, 0, 0, 412, 417, 3, 72, 36, 0, 413, 414, 5, 40, 0, 0, 414, 416, 3, 72, 36, 0, 415, 413, 1, 0, 0, 0, 416, 419, 1, 0, 0, 0, 417, 415, 1, 0, 0, 0, 417, 418, 1, 0, 0, 0, 418, 59, 1, 0, 0, 0, 419, 417, 1, 0, 0, 0, 420, 425, 3, 66, 33, 0, 421, 422, 5, 40, 0, 0, 422, 424, 3, 66, 33, 0, 423, 421, 1, 0, 0, 0, 424, 427, 1, 0, 0, 0, 425, 423, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 61, 1, 0, 0, 0, 427, 425, 1, 0, 0, 0, 428, 433, 3, 60, 30, 0, 429, 430, 5, 38, 0, 0, 430, 432, 3, 60, 30, 0, 431, 429, 1, 0, 0, 0, 432, 435, 1, 0, 0, 0, 433, 431, 1, 0, 0, 0, 433, 434, 1, 0, 0, 0, 434, 63, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 436, 437, 7, 3, 0, 0, 437, 65, 1, 0, 0, 0, 438, 442, 5, 86, 0, 0, 439, 440, 4, 33, 9, 0, 440, 442, 3, 70, 35, 0, 441, 438, 1, 0, 0, 0, 441, 439, 1, 0, 0, 0, 442, 67, 1, 0, 0, 0, 443, 486, 5, 49, 0, 0, 444, 445, 3, 104, 52, 0, 445, 446, 5, 73, 0, 0, 446, 486, 1, 0, 0, 0, 447, 486, 3, 102, 51, 0, 448, 486, 3, 104, 52, 0, 449, 486, 3, 98, 49, 0, 450, 486, 3, 70, 35, 0, 451, 486, 3, 106, 53, 0, 452, 453, 5, 71, 0, 0, 453, 458, 3, 100, 50, 0, 454, 455, 5, 38, 0, 0, 455, 457, 3, 100, 50, 0, 456, 454, 1, 0, 0, 0, 457, 460, 1, 0, 0, 0, 458, 456, 1, 0, 0, 0, 458, 459, 1, 0, 0, 0, 459, 461, 1, 0, 0, 0, 460, 458, 1, 0, 0, 0, 461, 462, 5, 72, 0, 0, 462, 486, 1, 0, 0, 0, 463, 464, 5, 71, 0, 0, 464, 469, 3, 98, 49, 0, 465, 466, 5, 38, 0, 0, 466, 468, 3, 98, 49, 0, 467, 465, 1, 0, 0, 0, 468, 471, 1, 0, 0, 0, 469, 467, 1, 0, 0, 0, 469, 470, 1, 0, 0, 0, 470, 472, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 472, 473, 5, 72, 0, 0, 473, 486, 1, 0, 0, 0, 474, 475, 5, 71, 0, 0, 475, 480, 3, 106, 53, 0, 476, 477, 5, 38, 0, 0, 477, 479, 3, 106, 53, 0, 478, 476, 1, 0, 0, 0, 479, 482, 1, 0, 0, 0, 480, 478, 1, 0, 0, 0, 480, 481, 1, 0, 0, 0, 481, 483, 1, 0, 0, 0, 482, 480, 1, 0, 0, 0, 483, 484, 5, 72, 0, 0, 484, 486, 1, 0, 0, 0, 485, 443, 1, 0, 0, 0, 485, 444, 1, 0, 0, 0, 485, 447, 1, 0, 0, 0, 485, 448, 1, 0, 0, 0, 485, 449, 1, 0, 0, 0, 485, 450, 1, 0, 0, 0, 485, 451, 1, 0, 0, 0, 485, 452, 1, 0, 0, 0, 485, 463, 1, 0, 0, 0, 485, 474, 1, 0, 0, 0, 486, 69, 1, 0, 0, 0, 487, 490, 5, 52, 0, 0, 488, 490, 5, 70, 0, 0, 489, 487, 1, 0, 0, 0, 489, 488, 1, 0, 0, 0, 490, 71, 1, 0, 0, 0, 491, 495, 3, 64, 32, 0, 492, 493, 4, 36, 10, 0, 493, 495, 3, 70, 35, 0, 494, 491, 1, 0, 0, 0, 494, 492, 1, 0, 0, 0, 495, 73, 1, 0, 0, 0, 496, 497, 5, 9, 0, 0, 497, 498, 5, 30, 0, 0, 498, 75, 1, 0, 0, 0, 499, 500, 5, 14, 0, 0, 500, 505, 3, 78, 39, 0, 501, 502, 5, 38, 0, 0, 502, 504, 3, 78, 39, 0, 503, 501, 1, 0, 0, 0, 504, 507, 1, 0, 0, 0, 505, 503, 1, 0, 0, 0, 505, 506, 1, 0, 0, 0, 506, 77, 1, 0, 0, 0, 507, 505, 1, 0, 0, 0, 508, 510, 3, 10, 5, 0, 509, 511, 7, 4, 0, 0, 510, 509, 1, 0, 0, 0, 510, 511, 1, 0, 0, 0, 511, 514, 1, 0, 0, 0, 512, 513, 5, 50, 0, 0, 513, 515, 7, 5, 0, 0, 514, 512, 1, 0, 0, 0, 514, 515, 1, 0, 0, 0, 515, 79, 1, 0, 0, 0, 516, 517, 5, 8, 0, 0, 517, 518, 3, 62, 31, 0, 518, 81, 1, 0, 0, 0, 519, 520, 5, 2, 0, 0, 520, 521, 3, 62, 31, 0, 521, 83, 1, 0, 0, 0, 522, 523, 5, 11, 0, 0, 523, 528, 3, 86, 43, 0, 524, 525, 5, 38, 0, 0, 525, 527, 3, 86, 43, 0, 526, 524, 1, 0, 0, 0, 527, 530, 1, 0, 0, 0, 528, 526, 1, 0, 0, 0, 528, 529, 1, 0, 0, 0, 529, 85, 1, 0, 0, 0, 530, 528, 1, 0, 0, 0, 531, 532, 3, 60, 30, 0, 532, 533, 5, 90, 0, 0, 533, 534, 3, 60, 30, 0, 534, 87, 1, 0, 0, 0, 535, 536, 5, 1, 0, 0, 536, 537, 3, 20, 10, 0, 537, 539, 3, 106, 53, 0, 538, 540, 3, 94, 47, 0, 539, 538, 1, 0, 0, 0, 539, 540, 1, 0, 0, 0, 540, 89, 1, 0, 0, 0, 541, 542, 5, 7, 0, 0, 542, 543, 3, 20, 10, 0, 543, 544, 3, 106, 53, 0, 544, 91, 1, 0, 0, 0, 545, 546, 5, 10, 0, 0, 546, 547, 3, 58, 29, 0, 547, 93, 1, 0, 0, 0, 548, 553, 3, 96, 48, 0, 549, 550, 5, 38, 0, 0, 550, 552, 3, 96, 48, 0, 551, 549, 1, 0, 0, 0, 552, 555, 1, 0, 0, 0, 553, 551, 1, 0, 0, 0, 553, 554, 1, 0, 0, 0, 554, 95, 1, 0, 0, 0, 555, 553, 1, 0, 0, 0, 556, 557, 3, 64, 32, 0, 557, 558, 5, 35, 0, 0, 558, 559, 3, 68, 34, 0, 559, 97, 1, 0, 0, 0, 560, 561, 7, 6, 0, 0, 561, 99, 1, 0, 0, 0, 562, 565, 3, 102, 51, 0, 563, 565, 3, 104, 52, 0, 564, 562, 1, 0, 0, 0, 564, 563, 1, 0, 0, 0, 565, 101, 1, 0, 0, 0, 566, 568, 7, 0, 0, 0, 567, 566, 1, 0, 0, 0, 567, 568, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, 569, 570, 5, 31, 0, 0, 570, 103, 1, 0, 0, 0, 571, 573, 7, 0, 0, 0, 572, 571, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 574, 1, 0, 0, 0, 574, 575, 5, 30, 0, 0, 575, 105, 1, 0, 0, 0, 576, 577, 5, 29, 0, 0, 577, 107, 1, 0, 0, 0, 578, 579, 7, 7, 0, 0, 579, 109, 1, 0, 0, 0, 580, 581, 5, 5, 0, 0, 581, 582, 3, 112, 56, 0, 582, 111, 1, 0, 0, 0, 583, 584, 5, 71, 0, 0, 584, 585, 3, 2, 1, 0, 585, 586, 5, 72, 0, 0, 586, 113, 1, 0, 0, 0, 587, 588, 5, 13, 0, 0, 588, 589, 5, 106, 0, 0, 589, 115, 1, 0, 0, 0, 590, 591, 5, 3, 0, 0, 591, 594, 5, 96, 0, 0, 592, 593, 5, 94, 0, 0, 593, 595, 3, 60, 30, 0, 594, 592, 1, 0, 0, 0, 594, 595, 1, 0, 0, 0, 595, 605, 1, 0, 0, 0, 596, 597, 5, 95, 0, 0, 597, 602, 3, 118, 59, 0, 598, 599, 5, 38, 0, 0, 599, 601, 3, 118, 59, 0, 600, 598, 1, 0, 0, 0, 601, 604, 1, 0, 0, 0, 602, 600, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 606, 1, 0, 0, 0, 604, 602, 1, 0, 0, 0, 605, 596, 1, 0, 0, 0, 605, 606, 1, 0, 0, 0, 606, 117, 1, 0, 0, 0, 607, 608, 3, 60, 30, 0, 608, 609, 5, 35, 0, 0, 609, 611, 1, 0, 0, 0, 610, 607, 1, 0, 0, 0, 610, 611, 1, 0, 0, 0, 611, 612, 1, 0, 0, 0, 612, 613, 3, 60, 30, 0, 613, 119, 1, 0, 0, 0, 614, 615, 5, 19, 0, 0, 615, 616, 3, 40, 20, 0, 616, 617, 5, 94, 0, 0, 617, 618, 3, 62, 31, 0, 618, 121, 1, 0, 0, 0, 619, 620, 5, 18, 0, 0, 620, 623, 3, 54, 27, 0, 621, 622, 5, 32, 0, 0, 622, 624, 3, 34, 17, 0, 623, 621, 1, 0, 0, 0, 623, 624, 1, 0, 0, 0, 624, 123, 1, 0, 0, 0, 625, 626, 7, 8, 0, 0, 626, 627, 5, 120, 0, 0, 627, 628, 3, 126, 63, 0, 628, 629, 3, 128, 64, 0, 629, 125, 1, 0, 0, 0, 630, 631, 3, 40, 20, 0, 631, 127, 1, 0, 0, 0, 632, 633, 5, 94, 0, 0, 633, 638, 3, 130, 65, 0, 634, 635, 5, 38, 0, 0, 635, 637, 3, 130, 65, 0, 636, 634, 1, 0, 0, 0, 637, 640, 1, 0, 0, 0, 638, 636, 1, 0, 0, 0, 638, 639, 1, 0, 0, 0, 639, 129, 1, 0, 0, 0, 640, 638, 1, 0, 0, 0, 641, 642, 3, 16, 8, 0, 642, 131, 1, 0, 0, 0, 61, 143, 152, 171, 183, 192, 200, 205, 213, 215, 220, 227, 232, 237, 247, 253, 261, 263, 274, 281, 292, 297, 299, 311, 330, 336, 346, 350, 355, 369, 378, 382, 386, 393, 397, 404, 410, 417, 425, 433, 441, 458, 469, 480, 485, 489, 494, 505, 510, 514, 528, 539, 553, 564, 567, 572, 594, 602, 605, 610, 623, 638] \ No newline at end of file diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.tokens b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.tokens index 366b455f16402..02af324872fc0 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.tokens +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.tokens @@ -14,110 +14,110 @@ SHOW=13 SORT=14 STATS=15 WHERE=16 -DEV_INLINESTATS=17 -DEV_LOOKUP=18 -DEV_METRICS=19 -DEV_JOIN=20 +JOIN_LOOKUP=17 +DEV_INLINESTATS=18 +DEV_LOOKUP=19 +DEV_METRICS=20 DEV_JOIN_FULL=21 DEV_JOIN_LEFT=22 DEV_JOIN_RIGHT=23 -DEV_JOIN_LOOKUP=24 -UNKNOWN_CMD=25 -LINE_COMMENT=26 -MULTILINE_COMMENT=27 -WS=28 -PIPE=29 -QUOTED_STRING=30 -INTEGER_LITERAL=31 -DECIMAL_LITERAL=32 -BY=33 -AND=34 -ASC=35 -ASSIGN=36 -CAST_OP=37 -COLON=38 -COMMA=39 -DESC=40 -DOT=41 -FALSE=42 -FIRST=43 -IN=44 -IS=45 -LAST=46 -LIKE=47 -LP=48 -NOT=49 -NULL=50 -NULLS=51 -OR=52 -PARAM=53 -RLIKE=54 -RP=55 -TRUE=56 -EQ=57 -CIEQ=58 -NEQ=59 -LT=60 -LTE=61 -GT=62 -GTE=63 -PLUS=64 -MINUS=65 -ASTERISK=66 -SLASH=67 -PERCENT=68 -LEFT_BRACES=69 -RIGHT_BRACES=70 -NAMED_OR_POSITIONAL_PARAM=71 -OPENING_BRACKET=72 -CLOSING_BRACKET=73 -UNQUOTED_IDENTIFIER=74 -QUOTED_IDENTIFIER=75 -EXPR_LINE_COMMENT=76 -EXPR_MULTILINE_COMMENT=77 -EXPR_WS=78 -EXPLAIN_WS=79 -EXPLAIN_LINE_COMMENT=80 -EXPLAIN_MULTILINE_COMMENT=81 -METADATA=82 -UNQUOTED_SOURCE=83 -FROM_LINE_COMMENT=84 -FROM_MULTILINE_COMMENT=85 -FROM_WS=86 -ID_PATTERN=87 -PROJECT_LINE_COMMENT=88 -PROJECT_MULTILINE_COMMENT=89 -PROJECT_WS=90 -AS=91 -RENAME_LINE_COMMENT=92 -RENAME_MULTILINE_COMMENT=93 -RENAME_WS=94 -ON=95 -WITH=96 -ENRICH_POLICY_NAME=97 -ENRICH_LINE_COMMENT=98 -ENRICH_MULTILINE_COMMENT=99 -ENRICH_WS=100 -ENRICH_FIELD_LINE_COMMENT=101 -ENRICH_FIELD_MULTILINE_COMMENT=102 -ENRICH_FIELD_WS=103 -MVEXPAND_LINE_COMMENT=104 -MVEXPAND_MULTILINE_COMMENT=105 -MVEXPAND_WS=106 -INFO=107 -SHOW_LINE_COMMENT=108 -SHOW_MULTILINE_COMMENT=109 -SHOW_WS=110 -SETTING=111 -SETTING_LINE_COMMENT=112 -SETTTING_MULTILINE_COMMENT=113 -SETTING_WS=114 -LOOKUP_LINE_COMMENT=115 -LOOKUP_MULTILINE_COMMENT=116 -LOOKUP_WS=117 -LOOKUP_FIELD_LINE_COMMENT=118 -LOOKUP_FIELD_MULTILINE_COMMENT=119 -LOOKUP_FIELD_WS=120 +UNKNOWN_CMD=24 +LINE_COMMENT=25 +MULTILINE_COMMENT=26 +WS=27 +PIPE=28 +QUOTED_STRING=29 +INTEGER_LITERAL=30 +DECIMAL_LITERAL=31 +BY=32 +AND=33 +ASC=34 +ASSIGN=35 +CAST_OP=36 +COLON=37 +COMMA=38 +DESC=39 +DOT=40 +FALSE=41 +FIRST=42 +IN=43 +IS=44 +LAST=45 +LIKE=46 +LP=47 +NOT=48 +NULL=49 +NULLS=50 +OR=51 +PARAM=52 +RLIKE=53 +RP=54 +TRUE=55 +EQ=56 +CIEQ=57 +NEQ=58 +LT=59 +LTE=60 +GT=61 +GTE=62 +PLUS=63 +MINUS=64 +ASTERISK=65 +SLASH=66 +PERCENT=67 +LEFT_BRACES=68 +RIGHT_BRACES=69 +NAMED_OR_POSITIONAL_PARAM=70 +OPENING_BRACKET=71 +CLOSING_BRACKET=72 +UNQUOTED_IDENTIFIER=73 +QUOTED_IDENTIFIER=74 +EXPR_LINE_COMMENT=75 +EXPR_MULTILINE_COMMENT=76 +EXPR_WS=77 +EXPLAIN_WS=78 +EXPLAIN_LINE_COMMENT=79 +EXPLAIN_MULTILINE_COMMENT=80 +METADATA=81 +UNQUOTED_SOURCE=82 +FROM_LINE_COMMENT=83 +FROM_MULTILINE_COMMENT=84 +FROM_WS=85 +ID_PATTERN=86 +PROJECT_LINE_COMMENT=87 +PROJECT_MULTILINE_COMMENT=88 +PROJECT_WS=89 +AS=90 +RENAME_LINE_COMMENT=91 +RENAME_MULTILINE_COMMENT=92 +RENAME_WS=93 +ON=94 +WITH=95 +ENRICH_POLICY_NAME=96 +ENRICH_LINE_COMMENT=97 +ENRICH_MULTILINE_COMMENT=98 +ENRICH_WS=99 +ENRICH_FIELD_LINE_COMMENT=100 +ENRICH_FIELD_MULTILINE_COMMENT=101 +ENRICH_FIELD_WS=102 +MVEXPAND_LINE_COMMENT=103 +MVEXPAND_MULTILINE_COMMENT=104 +MVEXPAND_WS=105 +INFO=106 +SHOW_LINE_COMMENT=107 +SHOW_MULTILINE_COMMENT=108 +SHOW_WS=109 +SETTING=110 +SETTING_LINE_COMMENT=111 +SETTTING_MULTILINE_COMMENT=112 +SETTING_WS=113 +LOOKUP_LINE_COMMENT=114 +LOOKUP_MULTILINE_COMMENT=115 +LOOKUP_WS=116 +LOOKUP_FIELD_LINE_COMMENT=117 +LOOKUP_FIELD_MULTILINE_COMMENT=118 +LOOKUP_FIELD_WS=119 +JOIN=120 USING=121 JOIN_LINE_COMMENT=122 JOIN_MULTILINE_COMMENT=123 @@ -144,47 +144,51 @@ CLOSING_METRICS_WS=130 'sort'=14 'stats'=15 'where'=16 -'|'=29 -'by'=33 -'and'=34 -'asc'=35 -'='=36 -'::'=37 -':'=38 -','=39 -'desc'=40 -'.'=41 -'false'=42 -'first'=43 -'in'=44 -'is'=45 -'last'=46 -'like'=47 -'('=48 -'not'=49 -'null'=50 -'nulls'=51 -'or'=52 -'?'=53 -'rlike'=54 -')'=55 -'true'=56 -'=='=57 -'=~'=58 -'!='=59 -'<'=60 -'<='=61 -'>'=62 -'>='=63 -'+'=64 -'-'=65 -'*'=66 -'/'=67 -'%'=68 -']'=73 -'metadata'=82 -'as'=91 -'on'=95 -'with'=96 -'info'=107 +'lookup'=17 +'|'=28 +'by'=32 +'and'=33 +'asc'=34 +'='=35 +'::'=36 +':'=37 +','=38 +'desc'=39 +'.'=40 +'false'=41 +'first'=42 +'in'=43 +'is'=44 +'last'=45 +'like'=46 +'('=47 +'not'=48 +'null'=49 +'nulls'=50 +'or'=51 +'?'=52 +'rlike'=53 +')'=54 +'true'=55 +'=='=56 +'=~'=57 +'!='=58 +'<'=59 +'<='=60 +'>'=61 +'>='=62 +'+'=63 +'-'=64 +'*'=65 +'/'=66 +'%'=67 +'{'=68 +'}'=69 +']'=72 +'metadata'=81 +'as'=90 +'on'=94 +'with'=95 +'info'=106 +'join'=120 'USING'=121 diff --git a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.ts b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.ts index 58d8ef5c61e8f..e07417973a5db 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/antlr/esql_parser.ts @@ -44,110 +44,110 @@ export default class esql_parser extends parser_config { public static readonly SORT = 14; public static readonly STATS = 15; public static readonly WHERE = 16; - public static readonly DEV_INLINESTATS = 17; - public static readonly DEV_LOOKUP = 18; - public static readonly DEV_METRICS = 19; - public static readonly DEV_JOIN = 20; + public static readonly JOIN_LOOKUP = 17; + public static readonly DEV_INLINESTATS = 18; + public static readonly DEV_LOOKUP = 19; + public static readonly DEV_METRICS = 20; public static readonly DEV_JOIN_FULL = 21; public static readonly DEV_JOIN_LEFT = 22; public static readonly DEV_JOIN_RIGHT = 23; - public static readonly DEV_JOIN_LOOKUP = 24; - public static readonly UNKNOWN_CMD = 25; - public static readonly LINE_COMMENT = 26; - public static readonly MULTILINE_COMMENT = 27; - public static readonly WS = 28; - public static readonly PIPE = 29; - public static readonly QUOTED_STRING = 30; - public static readonly INTEGER_LITERAL = 31; - public static readonly DECIMAL_LITERAL = 32; - public static readonly BY = 33; - public static readonly AND = 34; - public static readonly ASC = 35; - public static readonly ASSIGN = 36; - public static readonly CAST_OP = 37; - public static readonly COLON = 38; - public static readonly COMMA = 39; - public static readonly DESC = 40; - public static readonly DOT = 41; - public static readonly FALSE = 42; - public static readonly FIRST = 43; - public static readonly IN = 44; - public static readonly IS = 45; - public static readonly LAST = 46; - public static readonly LIKE = 47; - public static readonly LP = 48; - public static readonly NOT = 49; - public static readonly NULL = 50; - public static readonly NULLS = 51; - public static readonly OR = 52; - public static readonly PARAM = 53; - public static readonly RLIKE = 54; - public static readonly RP = 55; - public static readonly TRUE = 56; - public static readonly EQ = 57; - public static readonly CIEQ = 58; - public static readonly NEQ = 59; - public static readonly LT = 60; - public static readonly LTE = 61; - public static readonly GT = 62; - public static readonly GTE = 63; - public static readonly PLUS = 64; - public static readonly MINUS = 65; - public static readonly ASTERISK = 66; - public static readonly SLASH = 67; - public static readonly PERCENT = 68; - public static readonly LEFT_BRACES = 69; - public static readonly RIGHT_BRACES = 70; - public static readonly NAMED_OR_POSITIONAL_PARAM = 71; - public static readonly OPENING_BRACKET = 72; - public static readonly CLOSING_BRACKET = 73; - public static readonly UNQUOTED_IDENTIFIER = 74; - public static readonly QUOTED_IDENTIFIER = 75; - public static readonly EXPR_LINE_COMMENT = 76; - public static readonly EXPR_MULTILINE_COMMENT = 77; - public static readonly EXPR_WS = 78; - public static readonly EXPLAIN_WS = 79; - public static readonly EXPLAIN_LINE_COMMENT = 80; - public static readonly EXPLAIN_MULTILINE_COMMENT = 81; - public static readonly METADATA = 82; - public static readonly UNQUOTED_SOURCE = 83; - public static readonly FROM_LINE_COMMENT = 84; - public static readonly FROM_MULTILINE_COMMENT = 85; - public static readonly FROM_WS = 86; - public static readonly ID_PATTERN = 87; - public static readonly PROJECT_LINE_COMMENT = 88; - public static readonly PROJECT_MULTILINE_COMMENT = 89; - public static readonly PROJECT_WS = 90; - public static readonly AS = 91; - public static readonly RENAME_LINE_COMMENT = 92; - public static readonly RENAME_MULTILINE_COMMENT = 93; - public static readonly RENAME_WS = 94; - public static readonly ON = 95; - public static readonly WITH = 96; - public static readonly ENRICH_POLICY_NAME = 97; - public static readonly ENRICH_LINE_COMMENT = 98; - public static readonly ENRICH_MULTILINE_COMMENT = 99; - public static readonly ENRICH_WS = 100; - public static readonly ENRICH_FIELD_LINE_COMMENT = 101; - public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 102; - public static readonly ENRICH_FIELD_WS = 103; - public static readonly MVEXPAND_LINE_COMMENT = 104; - public static readonly MVEXPAND_MULTILINE_COMMENT = 105; - public static readonly MVEXPAND_WS = 106; - public static readonly INFO = 107; - public static readonly SHOW_LINE_COMMENT = 108; - public static readonly SHOW_MULTILINE_COMMENT = 109; - public static readonly SHOW_WS = 110; - public static readonly SETTING = 111; - public static readonly SETTING_LINE_COMMENT = 112; - public static readonly SETTTING_MULTILINE_COMMENT = 113; - public static readonly SETTING_WS = 114; - public static readonly LOOKUP_LINE_COMMENT = 115; - public static readonly LOOKUP_MULTILINE_COMMENT = 116; - public static readonly LOOKUP_WS = 117; - public static readonly LOOKUP_FIELD_LINE_COMMENT = 118; - public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 119; - public static readonly LOOKUP_FIELD_WS = 120; + public static readonly UNKNOWN_CMD = 24; + public static readonly LINE_COMMENT = 25; + public static readonly MULTILINE_COMMENT = 26; + public static readonly WS = 27; + public static readonly PIPE = 28; + public static readonly QUOTED_STRING = 29; + public static readonly INTEGER_LITERAL = 30; + public static readonly DECIMAL_LITERAL = 31; + public static readonly BY = 32; + public static readonly AND = 33; + public static readonly ASC = 34; + public static readonly ASSIGN = 35; + public static readonly CAST_OP = 36; + public static readonly COLON = 37; + public static readonly COMMA = 38; + public static readonly DESC = 39; + public static readonly DOT = 40; + public static readonly FALSE = 41; + public static readonly FIRST = 42; + public static readonly IN = 43; + public static readonly IS = 44; + public static readonly LAST = 45; + public static readonly LIKE = 46; + public static readonly LP = 47; + public static readonly NOT = 48; + public static readonly NULL = 49; + public static readonly NULLS = 50; + public static readonly OR = 51; + public static readonly PARAM = 52; + public static readonly RLIKE = 53; + public static readonly RP = 54; + public static readonly TRUE = 55; + public static readonly EQ = 56; + public static readonly CIEQ = 57; + public static readonly NEQ = 58; + public static readonly LT = 59; + public static readonly LTE = 60; + public static readonly GT = 61; + public static readonly GTE = 62; + public static readonly PLUS = 63; + public static readonly MINUS = 64; + public static readonly ASTERISK = 65; + public static readonly SLASH = 66; + public static readonly PERCENT = 67; + public static readonly LEFT_BRACES = 68; + public static readonly RIGHT_BRACES = 69; + public static readonly NAMED_OR_POSITIONAL_PARAM = 70; + public static readonly OPENING_BRACKET = 71; + public static readonly CLOSING_BRACKET = 72; + public static readonly UNQUOTED_IDENTIFIER = 73; + public static readonly QUOTED_IDENTIFIER = 74; + public static readonly EXPR_LINE_COMMENT = 75; + public static readonly EXPR_MULTILINE_COMMENT = 76; + public static readonly EXPR_WS = 77; + public static readonly EXPLAIN_WS = 78; + public static readonly EXPLAIN_LINE_COMMENT = 79; + public static readonly EXPLAIN_MULTILINE_COMMENT = 80; + public static readonly METADATA = 81; + public static readonly UNQUOTED_SOURCE = 82; + public static readonly FROM_LINE_COMMENT = 83; + public static readonly FROM_MULTILINE_COMMENT = 84; + public static readonly FROM_WS = 85; + public static readonly ID_PATTERN = 86; + public static readonly PROJECT_LINE_COMMENT = 87; + public static readonly PROJECT_MULTILINE_COMMENT = 88; + public static readonly PROJECT_WS = 89; + public static readonly AS = 90; + public static readonly RENAME_LINE_COMMENT = 91; + public static readonly RENAME_MULTILINE_COMMENT = 92; + public static readonly RENAME_WS = 93; + public static readonly ON = 94; + public static readonly WITH = 95; + public static readonly ENRICH_POLICY_NAME = 96; + public static readonly ENRICH_LINE_COMMENT = 97; + public static readonly ENRICH_MULTILINE_COMMENT = 98; + public static readonly ENRICH_WS = 99; + public static readonly ENRICH_FIELD_LINE_COMMENT = 100; + public static readonly ENRICH_FIELD_MULTILINE_COMMENT = 101; + public static readonly ENRICH_FIELD_WS = 102; + public static readonly MVEXPAND_LINE_COMMENT = 103; + public static readonly MVEXPAND_MULTILINE_COMMENT = 104; + public static readonly MVEXPAND_WS = 105; + public static readonly INFO = 106; + public static readonly SHOW_LINE_COMMENT = 107; + public static readonly SHOW_MULTILINE_COMMENT = 108; + public static readonly SHOW_WS = 109; + public static readonly SETTING = 110; + public static readonly SETTING_LINE_COMMENT = 111; + public static readonly SETTTING_MULTILINE_COMMENT = 112; + public static readonly SETTING_WS = 113; + public static readonly LOOKUP_LINE_COMMENT = 114; + public static readonly LOOKUP_MULTILINE_COMMENT = 115; + public static readonly LOOKUP_WS = 116; + public static readonly LOOKUP_FIELD_LINE_COMMENT = 117; + public static readonly LOOKUP_FIELD_MULTILINE_COMMENT = 118; + public static readonly LOOKUP_FIELD_WS = 119; + public static readonly JOIN = 120; public static readonly USING = 121; public static readonly JOIN_LINE_COMMENT = 122; public static readonly JOIN_MULTILINE_COMMENT = 123; @@ -234,40 +234,39 @@ export default class esql_parser extends parser_config { "'rename'", "'row'", "'show'", "'sort'", "'stats'", - "'where'", null, + "'where'", "'lookup'", null, null, null, null, null, null, null, null, null, null, - null, "'|'", + "'|'", null, null, null, - null, "'by'", - "'and'", "'asc'", - "'='", "'::'", - "':'", "','", - "'desc'", "'.'", - "'false'", "'first'", - "'in'", "'is'", - "'last'", "'like'", - "'('", "'not'", - "'null'", "'nulls'", - "'or'", "'?'", - "'rlike'", "')'", - "'true'", "'=='", - "'=~'", "'!='", - "'<'", "'<='", - "'>'", "'>='", - "'+'", "'-'", - "'*'", "'/'", - "'%'", null, + "'by'", "'and'", + "'asc'", "'='", + "'::'", "':'", + "','", "'desc'", + "'.'", "'false'", + "'first'", "'in'", + "'is'", "'last'", + "'like'", "'('", + "'not'", "'null'", + "'nulls'", "'or'", + "'?'", "'rlike'", + "')'", "'true'", + "'=='", "'=~'", + "'!='", "'<'", + "'<='", "'>'", + "'>='", "'+'", + "'-'", "'*'", + "'/'", "'%'", + "'{'", "'}'", null, null, - null, "']'", + "']'", null, null, null, null, null, null, null, - null, null, - "'metadata'", + null, "'metadata'", null, null, null, null, null, null, @@ -287,7 +286,7 @@ export default class esql_parser extends parser_config { null, null, null, null, null, null, - "'USING'" ]; + "'join'", "'USING'" ]; public static readonly symbolicNames: (string | null)[] = [ null, "DISSECT", "DROP", "ENRICH", "EVAL", "EXPLAIN", @@ -297,14 +296,13 @@ export default class esql_parser extends parser_config { "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", + "JOIN_LOOKUP", "DEV_INLINESTATS", "DEV_LOOKUP", "DEV_METRICS", - "DEV_JOIN", "DEV_JOIN_FULL", "DEV_JOIN_LEFT", "DEV_JOIN_RIGHT", - "DEV_JOIN_LOOKUP", "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", @@ -380,7 +378,8 @@ export default class esql_parser extends parser_config { "LOOKUP_FIELD_LINE_COMMENT", "LOOKUP_FIELD_MULTILINE_COMMENT", "LOOKUP_FIELD_WS", - "USING", "JOIN_LINE_COMMENT", + "JOIN", "USING", + "JOIN_LINE_COMMENT", "JOIN_MULTILINE_COMMENT", "JOIN_WS", "METRICS_LINE_COMMENT", @@ -587,7 +586,7 @@ export default class esql_parser extends parser_config { let localctx: ProcessingCommandContext = new ProcessingCommandContext(this, this._ctx, this.state); this.enterRule(localctx, 6, esql_parser.RULE_processingCommand); try { - this.state = 172; + this.state = 171; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 2, this._ctx) ) { case 1: @@ -678,33 +677,29 @@ export default class esql_parser extends parser_config { this.enterOuterAlt(localctx, 13); { this.state = 166; - if (!(this.isDevVersion())) { - throw this.createFailedPredicateException("this.isDevVersion()"); - } - this.state = 167; - this.inlinestatsCommand(); + this.joinCommand(); } break; case 14: this.enterOuterAlt(localctx, 14); { - this.state = 168; + this.state = 167; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 169; - this.lookupCommand(); + this.state = 168; + this.inlinestatsCommand(); } break; case 15: this.enterOuterAlt(localctx, 15); { - this.state = 170; + this.state = 169; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 171; - this.joinCommand(); + this.state = 170; + this.lookupCommand(); } break; } @@ -730,9 +725,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 174; + this.state = 173; this.match(esql_parser.WHERE); - this.state = 175; + this.state = 174; this.booleanExpression(0); } } @@ -770,7 +765,7 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 206; + this.state = 205; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 6, this._ctx) ) { case 1: @@ -779,9 +774,9 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 178; + this.state = 177; this.match(esql_parser.NOT); - this.state = 179; + this.state = 178; this.booleanExpression(8); } break; @@ -790,7 +785,7 @@ export default class esql_parser extends parser_config { localctx = new BooleanDefaultContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 180; + this.state = 179; this.valueExpression(); } break; @@ -799,7 +794,7 @@ export default class esql_parser extends parser_config { localctx = new RegexExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 181; + this.state = 180; this.regexBooleanExpression(); } break; @@ -808,41 +803,41 @@ export default class esql_parser extends parser_config { localctx = new LogicalInContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 182; + this.state = 181; this.valueExpression(); - this.state = 184; + this.state = 183; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===49) { + if (_la===48) { { - this.state = 183; + this.state = 182; this.match(esql_parser.NOT); } } - this.state = 186; + this.state = 185; this.match(esql_parser.IN); - this.state = 187; + this.state = 186; this.match(esql_parser.LP); - this.state = 188; + this.state = 187; this.valueExpression(); - this.state = 193; + this.state = 192; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===39) { + while (_la===38) { { { - this.state = 189; + this.state = 188; this.match(esql_parser.COMMA); - this.state = 190; + this.state = 189; this.valueExpression(); } } - this.state = 195; + this.state = 194; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 196; + this.state = 195; this.match(esql_parser.RP); } break; @@ -851,21 +846,21 @@ export default class esql_parser extends parser_config { localctx = new IsNullContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 198; + this.state = 197; this.valueExpression(); - this.state = 199; + this.state = 198; this.match(esql_parser.IS); - this.state = 201; + this.state = 200; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===49) { + if (_la===48) { { - this.state = 200; + this.state = 199; this.match(esql_parser.NOT); } } - this.state = 203; + this.state = 202; this.match(esql_parser.NULL); } break; @@ -874,13 +869,13 @@ export default class esql_parser extends parser_config { localctx = new MatchExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 205; + this.state = 204; this.matchBooleanExpression(); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 216; + this.state = 215; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -890,7 +885,7 @@ export default class esql_parser extends parser_config { } _prevctx = localctx; { - this.state = 214; + this.state = 213; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 7, this._ctx) ) { case 1: @@ -898,13 +893,13 @@ export default class esql_parser extends parser_config { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 208; + this.state = 207; if (!(this.precpred(this._ctx, 5))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 5)"); } - this.state = 209; + this.state = 208; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.AND); - this.state = 210; + this.state = 209; (localctx as LogicalBinaryContext)._right = this.booleanExpression(6); } break; @@ -913,20 +908,20 @@ export default class esql_parser extends parser_config { localctx = new LogicalBinaryContext(this, new BooleanExpressionContext(this, _parentctx, _parentState)); (localctx as LogicalBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 211; + this.state = 210; if (!(this.precpred(this._ctx, 4))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 4)"); } - this.state = 212; + this.state = 211; (localctx as LogicalBinaryContext)._operator = this.match(esql_parser.OR); - this.state = 213; + this.state = 212; (localctx as LogicalBinaryContext)._right = this.booleanExpression(5); } break; } } } - this.state = 218; + this.state = 217; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 8, this._ctx); } @@ -952,48 +947,48 @@ export default class esql_parser extends parser_config { this.enterRule(localctx, 12, esql_parser.RULE_regexBooleanExpression); let _la: number; try { - this.state = 233; + this.state = 232; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 11, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 219; + this.state = 218; this.valueExpression(); - this.state = 221; + this.state = 220; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===49) { + if (_la===48) { { - this.state = 220; + this.state = 219; this.match(esql_parser.NOT); } } - this.state = 223; + this.state = 222; localctx._kind = this.match(esql_parser.LIKE); - this.state = 224; + this.state = 223; localctx._pattern = this.string_(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 226; + this.state = 225; this.valueExpression(); - this.state = 228; + this.state = 227; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===49) { + if (_la===48) { { - this.state = 227; + this.state = 226; this.match(esql_parser.NOT); } } - this.state = 230; + this.state = 229; localctx._kind = this.match(esql_parser.RLIKE); - this.state = 231; + this.state = 230; localctx._pattern = this.string_(); } break; @@ -1021,23 +1016,23 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 235; + this.state = 234; localctx._fieldExp = this.qualifiedName(); - this.state = 238; + this.state = 237; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===37) { + if (_la===36) { { - this.state = 236; + this.state = 235; this.match(esql_parser.CAST_OP); - this.state = 237; + this.state = 236; localctx._fieldType = this.dataType(); } } - this.state = 240; + this.state = 239; this.match(esql_parser.COLON); - this.state = 241; + this.state = 240; localctx._matchQuery = this.constant(); } } @@ -1060,14 +1055,14 @@ export default class esql_parser extends parser_config { let localctx: ValueExpressionContext = new ValueExpressionContext(this, this._ctx, this.state); this.enterRule(localctx, 16, esql_parser.RULE_valueExpression); try { - this.state = 248; + this.state = 247; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 13, this._ctx) ) { case 1: localctx = new ValueExpressionDefaultContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 243; + this.state = 242; this.operatorExpression(0); } break; @@ -1075,11 +1070,11 @@ export default class esql_parser extends parser_config { localctx = new ComparisonContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 244; + this.state = 243; (localctx as ComparisonContext)._left = this.operatorExpression(0); - this.state = 245; + this.state = 244; this.comparisonOperator(); - this.state = 246; + this.state = 245; (localctx as ComparisonContext)._right = this.operatorExpression(0); } break; @@ -1119,7 +1114,7 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 254; + this.state = 253; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 14, this._ctx) ) { case 1: @@ -1128,7 +1123,7 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 251; + this.state = 250; this.primaryExpression(0); } break; @@ -1137,23 +1132,23 @@ export default class esql_parser extends parser_config { localctx = new ArithmeticUnaryContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 252; + this.state = 251; (localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===64 || _la===65)) { + if(!(_la===63 || _la===64)) { (localctx as ArithmeticUnaryContext)._operator = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 253; + this.state = 252; this.operatorExpression(3); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 264; + this.state = 263; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 16, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1163,7 +1158,7 @@ export default class esql_parser extends parser_config { } _prevctx = localctx; { - this.state = 262; + this.state = 261; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 15, this._ctx) ) { case 1: @@ -1171,21 +1166,21 @@ export default class esql_parser extends parser_config { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 256; + this.state = 255; if (!(this.precpred(this._ctx, 2))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 2)"); } - this.state = 257; + this.state = 256; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(((((_la - 66)) & ~0x1F) === 0 && ((1 << (_la - 66)) & 7) !== 0))) { + if(!(((((_la - 65)) & ~0x1F) === 0 && ((1 << (_la - 65)) & 7) !== 0))) { (localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 258; + this.state = 257; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(3); } break; @@ -1194,28 +1189,28 @@ export default class esql_parser extends parser_config { localctx = new ArithmeticBinaryContext(this, new OperatorExpressionContext(this, _parentctx, _parentState)); (localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 259; + this.state = 258; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 260; + this.state = 259; (localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===64 || _la===65)) { + if(!(_la===63 || _la===64)) { (localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { this._errHandler.reportMatch(this); this.consume(); } - this.state = 261; + this.state = 260; (localctx as ArithmeticBinaryContext)._right = this.operatorExpression(2); } break; } } } - this.state = 266; + this.state = 265; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 16, this._ctx); } @@ -1254,7 +1249,7 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 275; + this.state = 274; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 17, this._ctx) ) { case 1: @@ -1263,7 +1258,7 @@ export default class esql_parser extends parser_config { this._ctx = localctx; _prevctx = localctx; - this.state = 268; + this.state = 267; this.constant(); } break; @@ -1272,7 +1267,7 @@ export default class esql_parser extends parser_config { localctx = new DereferenceContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 269; + this.state = 268; this.qualifiedName(); } break; @@ -1281,7 +1276,7 @@ export default class esql_parser extends parser_config { localctx = new FunctionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 270; + this.state = 269; this.functionExpression(); } break; @@ -1290,17 +1285,17 @@ export default class esql_parser extends parser_config { localctx = new ParenthesizedExpressionContext(this, localctx); this._ctx = localctx; _prevctx = localctx; - this.state = 271; + this.state = 270; this.match(esql_parser.LP); - this.state = 272; + this.state = 271; this.booleanExpression(0); - this.state = 273; + this.state = 272; this.match(esql_parser.RP); } break; } this._ctx.stop = this._input.LT(-1); - this.state = 282; + this.state = 281; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 18, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -1313,18 +1308,18 @@ export default class esql_parser extends parser_config { { localctx = new InlineCastContext(this, new PrimaryExpressionContext(this, _parentctx, _parentState)); this.pushNewRecursionContext(localctx, _startState, esql_parser.RULE_primaryExpression); - this.state = 277; + this.state = 276; if (!(this.precpred(this._ctx, 1))) { throw this.createFailedPredicateException("this.precpred(this._ctx, 1)"); } - this.state = 278; + this.state = 277; this.match(esql_parser.CAST_OP); - this.state = 279; + this.state = 278; this.dataType(); } } } - this.state = 284; + this.state = 283; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 18, this._ctx); } @@ -1353,50 +1348,50 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 285; + this.state = 284; this.functionName(); - this.state = 286; + this.state = 285; this.match(esql_parser.LP); - this.state = 300; + this.state = 299; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 21, this._ctx) ) { case 1: { - this.state = 287; + this.state = 286; this.match(esql_parser.ASTERISK); } break; case 2: { { - this.state = 288; + this.state = 287; this.booleanExpression(0); - this.state = 293; + this.state = 292; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 19, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 289; + this.state = 288; this.match(esql_parser.COMMA); - this.state = 290; + this.state = 289; this.booleanExpression(0); } } } - this.state = 295; + this.state = 294; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 19, this._ctx); } - this.state = 298; + this.state = 297; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===39) { + if (_la===38) { { - this.state = 296; + this.state = 295; this.match(esql_parser.COMMA); - this.state = 297; + this.state = 296; this.mapExpression(); } } @@ -1405,7 +1400,7 @@ export default class esql_parser extends parser_config { } break; } - this.state = 302; + this.state = 301; this.match(esql_parser.RP); } } @@ -1430,7 +1425,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 304; + this.state = 303; this.identifierOrParameter(); } } @@ -1456,31 +1451,27 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 306; - if (!(this.isDevVersion())) { - throw this.createFailedPredicateException("this.isDevVersion()"); - } - this.state = 307; + this.state = 305; this.match(esql_parser.LEFT_BRACES); - this.state = 308; + this.state = 306; this.entryExpression(); - this.state = 313; + this.state = 311; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===39) { + while (_la===38) { { { - this.state = 309; + this.state = 307; this.match(esql_parser.COMMA); - this.state = 310; + this.state = 308; this.entryExpression(); } } - this.state = 315; + this.state = 313; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 316; + this.state = 314; this.match(esql_parser.RIGHT_BRACES); } } @@ -1505,11 +1496,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 318; + this.state = 316; localctx._key = this.string_(); - this.state = 319; + this.state = 317; this.match(esql_parser.COLON); - this.state = 320; + this.state = 318; localctx._value = this.constant(); } } @@ -1535,7 +1526,7 @@ export default class esql_parser extends parser_config { localctx = new ToDataTypeContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 322; + this.state = 320; this.identifier(); } } @@ -1560,9 +1551,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 324; + this.state = 322; this.match(esql_parser.ROW); - this.state = 325; + this.state = 323; this.fields(); } } @@ -1588,23 +1579,23 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 327; + this.state = 325; this.field(); - this.state = 332; + this.state = 330; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 328; + this.state = 326; this.match(esql_parser.COMMA); - this.state = 329; + this.state = 327; this.field(); } } } - this.state = 334; + this.state = 332; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 23, this._ctx); } @@ -1631,19 +1622,19 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 338; + this.state = 336; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 24, this._ctx) ) { case 1: { - this.state = 335; + this.state = 333; this.qualifiedName(); - this.state = 336; + this.state = 334; this.match(esql_parser.ASSIGN); } break; } - this.state = 340; + this.state = 338; this.booleanExpression(0); } } @@ -1669,34 +1660,34 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 342; + this.state = 340; this.match(esql_parser.FROM); - this.state = 343; + this.state = 341; this.indexPattern(); - this.state = 348; + this.state = 346; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 344; + this.state = 342; this.match(esql_parser.COMMA); - this.state = 345; + this.state = 343; this.indexPattern(); } } } - this.state = 350; + this.state = 348; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 25, this._ctx); } - this.state = 352; + this.state = 350; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 26, this._ctx) ) { case 1: { - this.state = 351; + this.state = 349; this.metadata(); } break; @@ -1724,19 +1715,19 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 357; + this.state = 355; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 27, this._ctx) ) { case 1: { - this.state = 354; + this.state = 352; this.clusterString(); - this.state = 355; + this.state = 353; this.match(esql_parser.COLON); } break; } - this.state = 359; + this.state = 357; this.indexString(); } } @@ -1761,7 +1752,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 361; + this.state = 359; this.match(esql_parser.UNQUOTED_SOURCE); } } @@ -1787,9 +1778,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 363; + this.state = 361; _la = this._input.LA(1); - if(!(_la===30 || _la===83)) { + if(!(_la===29 || _la===82)) { this._errHandler.recoverInline(this); } else { @@ -1820,25 +1811,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 365; + this.state = 363; this.match(esql_parser.METADATA); - this.state = 366; + this.state = 364; this.match(esql_parser.UNQUOTED_SOURCE); - this.state = 371; + this.state = 369; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 367; + this.state = 365; this.match(esql_parser.COMMA); - this.state = 368; + this.state = 366; this.match(esql_parser.UNQUOTED_SOURCE); } } } - this.state = 373; + this.state = 371; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 28, this._ctx); } @@ -1866,46 +1857,46 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 374; + this.state = 372; this.match(esql_parser.DEV_METRICS); - this.state = 375; + this.state = 373; this.indexPattern(); - this.state = 380; + this.state = 378; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 29, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 376; + this.state = 374; this.match(esql_parser.COMMA); - this.state = 377; + this.state = 375; this.indexPattern(); } } } - this.state = 382; + this.state = 380; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 29, this._ctx); } - this.state = 384; + this.state = 382; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 30, this._ctx) ) { case 1: { - this.state = 383; + this.state = 381; localctx._aggregates = this.aggFields(); } break; } - this.state = 388; + this.state = 386; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 31, this._ctx) ) { case 1: { - this.state = 386; + this.state = 384; this.match(esql_parser.BY); - this.state = 387; + this.state = 385; localctx._grouping = this.fields(); } break; @@ -1933,9 +1924,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 390; + this.state = 388; this.match(esql_parser.EVAL); - this.state = 391; + this.state = 389; this.fields(); } } @@ -1960,26 +1951,26 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 393; + this.state = 391; this.match(esql_parser.STATS); - this.state = 395; + this.state = 393; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 32, this._ctx) ) { case 1: { - this.state = 394; + this.state = 392; localctx._stats = this.aggFields(); } break; } - this.state = 399; + this.state = 397; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 33, this._ctx) ) { case 1: { - this.state = 397; + this.state = 395; this.match(esql_parser.BY); - this.state = 398; + this.state = 396; localctx._grouping = this.fields(); } break; @@ -2008,23 +1999,23 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 401; + this.state = 399; this.aggField(); - this.state = 406; + this.state = 404; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 402; + this.state = 400; this.match(esql_parser.COMMA); - this.state = 403; + this.state = 401; this.aggField(); } } } - this.state = 408; + this.state = 406; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 34, this._ctx); } @@ -2051,16 +2042,16 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 409; + this.state = 407; this.field(); - this.state = 412; + this.state = 410; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 35, this._ctx) ) { case 1: { - this.state = 410; + this.state = 408; this.match(esql_parser.WHERE); - this.state = 411; + this.state = 409; this.booleanExpression(0); } break; @@ -2089,23 +2080,23 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 414; + this.state = 412; this.identifierOrParameter(); - this.state = 419; + this.state = 417; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 415; + this.state = 413; this.match(esql_parser.DOT); - this.state = 416; + this.state = 414; this.identifierOrParameter(); } } } - this.state = 421; + this.state = 419; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 36, this._ctx); } @@ -2133,23 +2124,23 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 422; + this.state = 420; this.identifierPattern(); - this.state = 427; + this.state = 425; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 423; + this.state = 421; this.match(esql_parser.DOT); - this.state = 424; + this.state = 422; this.identifierPattern(); } } } - this.state = 429; + this.state = 427; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 37, this._ctx); } @@ -2177,23 +2168,23 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 430; + this.state = 428; this.qualifiedNamePattern(); - this.state = 435; + this.state = 433; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 38, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 431; + this.state = 429; this.match(esql_parser.COMMA); - this.state = 432; + this.state = 430; this.qualifiedNamePattern(); } } } - this.state = 437; + this.state = 435; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 38, this._ctx); } @@ -2221,9 +2212,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 438; + this.state = 436; _la = this._input.LA(1); - if(!(_la===74 || _la===75)) { + if(!(_la===73 || _la===74)) { this._errHandler.recoverInline(this); } else { @@ -2251,24 +2242,24 @@ export default class esql_parser extends parser_config { let localctx: IdentifierPatternContext = new IdentifierPatternContext(this, this._ctx, this.state); this.enterRule(localctx, 66, esql_parser.RULE_identifierPattern); try { - this.state = 443; + this.state = 441; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 39, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 440; + this.state = 438; this.match(esql_parser.ID_PATTERN); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 441; + this.state = 439; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 442; + this.state = 440; this.parameter(); } break; @@ -2294,14 +2285,14 @@ export default class esql_parser extends parser_config { this.enterRule(localctx, 68, esql_parser.RULE_constant); let _la: number; try { - this.state = 487; + this.state = 485; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 43, this._ctx) ) { case 1: localctx = new NullLiteralContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 445; + this.state = 443; this.match(esql_parser.NULL); } break; @@ -2309,9 +2300,9 @@ export default class esql_parser extends parser_config { localctx = new QualifiedIntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 446; + this.state = 444; this.integerValue(); - this.state = 447; + this.state = 445; this.match(esql_parser.UNQUOTED_IDENTIFIER); } break; @@ -2319,7 +2310,7 @@ export default class esql_parser extends parser_config { localctx = new DecimalLiteralContext(this, localctx); this.enterOuterAlt(localctx, 3); { - this.state = 449; + this.state = 447; this.decimalValue(); } break; @@ -2327,7 +2318,7 @@ export default class esql_parser extends parser_config { localctx = new IntegerLiteralContext(this, localctx); this.enterOuterAlt(localctx, 4); { - this.state = 450; + this.state = 448; this.integerValue(); } break; @@ -2335,7 +2326,7 @@ export default class esql_parser extends parser_config { localctx = new BooleanLiteralContext(this, localctx); this.enterOuterAlt(localctx, 5); { - this.state = 451; + this.state = 449; this.booleanValue(); } break; @@ -2343,7 +2334,7 @@ export default class esql_parser extends parser_config { localctx = new InputParameterContext(this, localctx); this.enterOuterAlt(localctx, 6); { - this.state = 452; + this.state = 450; this.parameter(); } break; @@ -2351,7 +2342,7 @@ export default class esql_parser extends parser_config { localctx = new StringLiteralContext(this, localctx); this.enterOuterAlt(localctx, 7); { - this.state = 453; + this.state = 451; this.string_(); } break; @@ -2359,27 +2350,27 @@ export default class esql_parser extends parser_config { localctx = new NumericArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 8); { - this.state = 454; + this.state = 452; this.match(esql_parser.OPENING_BRACKET); - this.state = 455; + this.state = 453; this.numericValue(); - this.state = 460; + this.state = 458; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===39) { + while (_la===38) { { { - this.state = 456; + this.state = 454; this.match(esql_parser.COMMA); - this.state = 457; + this.state = 455; this.numericValue(); } } - this.state = 462; + this.state = 460; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 463; + this.state = 461; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2387,27 +2378,27 @@ export default class esql_parser extends parser_config { localctx = new BooleanArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 9); { - this.state = 465; + this.state = 463; this.match(esql_parser.OPENING_BRACKET); - this.state = 466; + this.state = 464; this.booleanValue(); - this.state = 471; + this.state = 469; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===39) { + while (_la===38) { { { - this.state = 467; + this.state = 465; this.match(esql_parser.COMMA); - this.state = 468; + this.state = 466; this.booleanValue(); } } - this.state = 473; + this.state = 471; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 474; + this.state = 472; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2415,27 +2406,27 @@ export default class esql_parser extends parser_config { localctx = new StringArrayLiteralContext(this, localctx); this.enterOuterAlt(localctx, 10); { - this.state = 476; + this.state = 474; this.match(esql_parser.OPENING_BRACKET); - this.state = 477; + this.state = 475; this.string_(); - this.state = 482; + this.state = 480; this._errHandler.sync(this); _la = this._input.LA(1); - while (_la===39) { + while (_la===38) { { { - this.state = 478; + this.state = 476; this.match(esql_parser.COMMA); - this.state = 479; + this.state = 477; this.string_(); } } - this.state = 484; + this.state = 482; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 485; + this.state = 483; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2460,22 +2451,22 @@ export default class esql_parser extends parser_config { let localctx: ParameterContext = new ParameterContext(this, this._ctx, this.state); this.enterRule(localctx, 70, esql_parser.RULE_parameter); try { - this.state = 491; + this.state = 489; this._errHandler.sync(this); switch (this._input.LA(1)) { - case 53: + case 52: localctx = new InputParamContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 489; + this.state = 487; this.match(esql_parser.PARAM); } break; - case 71: + case 70: localctx = new InputNamedOrPositionalParamContext(this, localctx); this.enterOuterAlt(localctx, 2); { - this.state = 490; + this.state = 488; this.match(esql_parser.NAMED_OR_POSITIONAL_PARAM); } break; @@ -2502,24 +2493,24 @@ export default class esql_parser extends parser_config { let localctx: IdentifierOrParameterContext = new IdentifierOrParameterContext(this, this._ctx, this.state); this.enterRule(localctx, 72, esql_parser.RULE_identifierOrParameter); try { - this.state = 496; + this.state = 494; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 45, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 493; + this.state = 491; this.identifier(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 494; + this.state = 492; if (!(this.isDevVersion())) { throw this.createFailedPredicateException("this.isDevVersion()"); } - this.state = 495; + this.state = 493; this.parameter(); } break; @@ -2546,9 +2537,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 498; + this.state = 496; this.match(esql_parser.LIMIT); - this.state = 499; + this.state = 497; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2574,25 +2565,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 501; + this.state = 499; this.match(esql_parser.SORT); - this.state = 502; + this.state = 500; this.orderExpression(); - this.state = 507; + this.state = 505; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 46, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 503; + this.state = 501; this.match(esql_parser.COMMA); - this.state = 504; + this.state = 502; this.orderExpression(); } } } - this.state = 509; + this.state = 507; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 46, this._ctx); } @@ -2620,17 +2611,17 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 510; + this.state = 508; this.booleanExpression(0); - this.state = 512; + this.state = 510; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 47, this._ctx) ) { case 1: { - this.state = 511; + this.state = 509; localctx._ordering = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===35 || _la===40)) { + if(!(_la===34 || _la===39)) { localctx._ordering = this._errHandler.recoverInline(this); } else { @@ -2640,17 +2631,17 @@ export default class esql_parser extends parser_config { } break; } - this.state = 516; + this.state = 514; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 48, this._ctx) ) { case 1: { - this.state = 514; + this.state = 512; this.match(esql_parser.NULLS); - this.state = 515; + this.state = 513; localctx._nullOrdering = this._input.LT(1); _la = this._input.LA(1); - if(!(_la===43 || _la===46)) { + if(!(_la===42 || _la===45)) { localctx._nullOrdering = this._errHandler.recoverInline(this); } else { @@ -2683,9 +2674,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 518; + this.state = 516; this.match(esql_parser.KEEP); - this.state = 519; + this.state = 517; this.qualifiedNamePatterns(); } } @@ -2710,9 +2701,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 521; + this.state = 519; this.match(esql_parser.DROP); - this.state = 522; + this.state = 520; this.qualifiedNamePatterns(); } } @@ -2738,25 +2729,25 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 524; + this.state = 522; this.match(esql_parser.RENAME); - this.state = 525; + this.state = 523; this.renameClause(); - this.state = 530; + this.state = 528; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 49, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 526; + this.state = 524; this.match(esql_parser.COMMA); - this.state = 527; + this.state = 525; this.renameClause(); } } } - this.state = 532; + this.state = 530; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 49, this._ctx); } @@ -2783,11 +2774,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 533; + this.state = 531; localctx._oldName = this.qualifiedNamePattern(); - this.state = 534; + this.state = 532; this.match(esql_parser.AS); - this.state = 535; + this.state = 533; localctx._newName = this.qualifiedNamePattern(); } } @@ -2812,18 +2803,18 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 537; + this.state = 535; this.match(esql_parser.DISSECT); - this.state = 538; + this.state = 536; this.primaryExpression(0); - this.state = 539; + this.state = 537; this.string_(); - this.state = 541; + this.state = 539; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 50, this._ctx) ) { case 1: { - this.state = 540; + this.state = 538; this.commandOptions(); } break; @@ -2851,11 +2842,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 543; + this.state = 541; this.match(esql_parser.GROK); - this.state = 544; + this.state = 542; this.primaryExpression(0); - this.state = 545; + this.state = 543; this.string_(); } } @@ -2880,9 +2871,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 547; + this.state = 545; this.match(esql_parser.MV_EXPAND); - this.state = 548; + this.state = 546; this.qualifiedName(); } } @@ -2908,23 +2899,23 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 550; + this.state = 548; this.commandOption(); - this.state = 555; + this.state = 553; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 51, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 551; + this.state = 549; this.match(esql_parser.COMMA); - this.state = 552; + this.state = 550; this.commandOption(); } } } - this.state = 557; + this.state = 555; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 51, this._ctx); } @@ -2951,11 +2942,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 558; + this.state = 556; this.identifier(); - this.state = 559; + this.state = 557; this.match(esql_parser.ASSIGN); - this.state = 560; + this.state = 558; this.constant(); } } @@ -2981,9 +2972,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 562; + this.state = 560; _la = this._input.LA(1); - if(!(_la===42 || _la===56)) { + if(!(_la===41 || _la===55)) { this._errHandler.recoverInline(this); } else { @@ -3011,20 +3002,20 @@ export default class esql_parser extends parser_config { let localctx: NumericValueContext = new NumericValueContext(this, this._ctx, this.state); this.enterRule(localctx, 100, esql_parser.RULE_numericValue); try { - this.state = 566; + this.state = 564; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 52, this._ctx) ) { case 1: this.enterOuterAlt(localctx, 1); { - this.state = 564; + this.state = 562; this.decimalValue(); } break; case 2: this.enterOuterAlt(localctx, 2); { - this.state = 565; + this.state = 563; this.integerValue(); } break; @@ -3052,14 +3043,14 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 569; + this.state = 567; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===64 || _la===65) { + if (_la===63 || _la===64) { { - this.state = 568; + this.state = 566; _la = this._input.LA(1); - if(!(_la===64 || _la===65)) { + if(!(_la===63 || _la===64)) { this._errHandler.recoverInline(this); } else { @@ -3069,7 +3060,7 @@ export default class esql_parser extends parser_config { } } - this.state = 571; + this.state = 569; this.match(esql_parser.DECIMAL_LITERAL); } } @@ -3095,14 +3086,14 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 574; + this.state = 572; this._errHandler.sync(this); _la = this._input.LA(1); - if (_la===64 || _la===65) { + if (_la===63 || _la===64) { { - this.state = 573; + this.state = 571; _la = this._input.LA(1); - if(!(_la===64 || _la===65)) { + if(!(_la===63 || _la===64)) { this._errHandler.recoverInline(this); } else { @@ -3112,7 +3103,7 @@ export default class esql_parser extends parser_config { } } - this.state = 576; + this.state = 574; this.match(esql_parser.INTEGER_LITERAL); } } @@ -3137,7 +3128,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 578; + this.state = 576; this.match(esql_parser.QUOTED_STRING); } } @@ -3163,9 +3154,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 580; + this.state = 578; _la = this._input.LA(1); - if(!(((((_la - 57)) & ~0x1F) === 0 && ((1 << (_la - 57)) & 125) !== 0))) { + if(!(((((_la - 56)) & ~0x1F) === 0 && ((1 << (_la - 56)) & 125) !== 0))) { this._errHandler.recoverInline(this); } else { @@ -3195,9 +3186,9 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 582; + this.state = 580; this.match(esql_parser.EXPLAIN); - this.state = 583; + this.state = 581; this.subqueryExpression(); } } @@ -3222,11 +3213,11 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 585; + this.state = 583; this.match(esql_parser.OPENING_BRACKET); - this.state = 586; + this.state = 584; this.query(0); - this.state = 587; + this.state = 585; this.match(esql_parser.CLOSING_BRACKET); } } @@ -3252,9 +3243,9 @@ export default class esql_parser extends parser_config { localctx = new ShowInfoContext(this, localctx); this.enterOuterAlt(localctx, 1); { - this.state = 589; + this.state = 587; this.match(esql_parser.SHOW); - this.state = 590; + this.state = 588; this.match(esql_parser.INFO); } } @@ -3280,46 +3271,46 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 592; + this.state = 590; this.match(esql_parser.ENRICH); - this.state = 593; + this.state = 591; localctx._policyName = this.match(esql_parser.ENRICH_POLICY_NAME); - this.state = 596; + this.state = 594; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 55, this._ctx) ) { case 1: { - this.state = 594; + this.state = 592; this.match(esql_parser.ON); - this.state = 595; + this.state = 593; localctx._matchField = this.qualifiedNamePattern(); } break; } - this.state = 607; + this.state = 605; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 57, this._ctx) ) { case 1: { - this.state = 598; + this.state = 596; this.match(esql_parser.WITH); - this.state = 599; + this.state = 597; this.enrichWithClause(); - this.state = 604; + this.state = 602; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 56, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 600; + this.state = 598; this.match(esql_parser.COMMA); - this.state = 601; + this.state = 599; this.enrichWithClause(); } } } - this.state = 606; + this.state = 604; this._errHandler.sync(this); _alt = this._interp.adaptivePredict(this._input, 56, this._ctx); } @@ -3349,19 +3340,19 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 612; + this.state = 610; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 58, this._ctx) ) { case 1: { - this.state = 609; + this.state = 607; localctx._newName = this.qualifiedNamePattern(); - this.state = 610; + this.state = 608; this.match(esql_parser.ASSIGN); } break; } - this.state = 614; + this.state = 612; localctx._enrichField = this.qualifiedNamePattern(); } } @@ -3386,13 +3377,13 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 616; + this.state = 614; this.match(esql_parser.DEV_LOOKUP); - this.state = 617; + this.state = 615; localctx._tableName = this.indexPattern(); - this.state = 618; + this.state = 616; this.match(esql_parser.ON); - this.state = 619; + this.state = 617; localctx._matchFields = this.qualifiedNamePatterns(); } } @@ -3417,18 +3408,18 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 621; + this.state = 619; this.match(esql_parser.DEV_INLINESTATS); - this.state = 622; + this.state = 620; localctx._stats = this.aggFields(); - this.state = 625; + this.state = 623; this._errHandler.sync(this); switch ( this._interp.adaptivePredict(this._input, 59, this._ctx) ) { case 1: { - this.state = 623; + this.state = 621; this.match(esql_parser.BY); - this.state = 624; + this.state = 622; localctx._grouping = this.fields(); } break; @@ -3457,29 +3448,21 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 628; - this._errHandler.sync(this); + this.state = 625; + localctx._type_ = this._input.LT(1); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & 29360128) !== 0)) { - { - this.state = 627; - localctx._type_ = this._input.LT(1); - _la = this._input.LA(1); - if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 29360128) !== 0))) { - localctx._type_ = this._errHandler.recoverInline(this); - } - else { - this._errHandler.reportMatch(this); - this.consume(); - } - } + if(!((((_la) & ~0x1F) === 0 && ((1 << _la) & 12713984) !== 0))) { + localctx._type_ = this._errHandler.recoverInline(this); } - - this.state = 630; - this.match(esql_parser.DEV_JOIN); - this.state = 631; + else { + this._errHandler.reportMatch(this); + this.consume(); + } + this.state = 626; + this.match(esql_parser.JOIN); + this.state = 627; this.joinTarget(); - this.state = 632; + this.state = 628; this.joinCondition(); } } @@ -3501,24 +3484,11 @@ export default class esql_parser extends parser_config { public joinTarget(): JoinTargetContext { let localctx: JoinTargetContext = new JoinTargetContext(this, this._ctx, this.state); this.enterRule(localctx, 126, esql_parser.RULE_joinTarget); - let _la: number; try { this.enterOuterAlt(localctx, 1); { - this.state = 634; + this.state = 630; localctx._index = this.indexPattern(); - this.state = 637; - this._errHandler.sync(this); - _la = this._input.LA(1); - if (_la===91) { - { - this.state = 635; - this.match(esql_parser.AS); - this.state = 636; - localctx._alias = this.identifier(); - } - } - } } catch (re) { @@ -3543,27 +3513,27 @@ export default class esql_parser extends parser_config { let _alt: number; this.enterOuterAlt(localctx, 1); { - this.state = 639; + this.state = 632; this.match(esql_parser.ON); - this.state = 640; + this.state = 633; this.joinPredicate(); - this.state = 645; + this.state = 638; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 62, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 60, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 641; + this.state = 634; this.match(esql_parser.COMMA); - this.state = 642; + this.state = 635; this.joinPredicate(); } } } - this.state = 647; + this.state = 640; this._errHandler.sync(this); - _alt = this._interp.adaptivePredict(this._input, 62, this._ctx); + _alt = this._interp.adaptivePredict(this._input, 60, this._ctx); } } } @@ -3588,7 +3558,7 @@ export default class esql_parser extends parser_config { try { this.enterOuterAlt(localctx, 1); { - this.state = 648; + this.state = 641; this.valueExpression(); } } @@ -3621,8 +3591,6 @@ export default class esql_parser extends parser_config { return this.operatorExpression_sempred(localctx as OperatorExpressionContext, predIndex); case 10: return this.primaryExpression_sempred(localctx as PrimaryExpressionContext, predIndex); - case 13: - return this.mapExpression_sempred(localctx as MapExpressionContext, predIndex); case 33: return this.identifierPattern_sempred(localctx as IdentifierPatternContext, predIndex); case 36: @@ -3650,59 +3618,50 @@ export default class esql_parser extends parser_config { return this.isDevVersion(); case 3: return this.isDevVersion(); - case 4: - return this.isDevVersion(); } return true; } private booleanExpression_sempred(localctx: BooleanExpressionContext, predIndex: number): boolean { switch (predIndex) { - case 5: + case 4: return this.precpred(this._ctx, 5); - case 6: + case 5: return this.precpred(this._ctx, 4); } return true; } private operatorExpression_sempred(localctx: OperatorExpressionContext, predIndex: number): boolean { switch (predIndex) { - case 7: + case 6: return this.precpred(this._ctx, 2); - case 8: + case 7: return this.precpred(this._ctx, 1); } return true; } private primaryExpression_sempred(localctx: PrimaryExpressionContext, predIndex: number): boolean { switch (predIndex) { - case 9: + case 8: return this.precpred(this._ctx, 1); } return true; } - private mapExpression_sempred(localctx: MapExpressionContext, predIndex: number): boolean { - switch (predIndex) { - case 10: - return this.isDevVersion(); - } - return true; - } private identifierPattern_sempred(localctx: IdentifierPatternContext, predIndex: number): boolean { switch (predIndex) { - case 11: + case 9: return this.isDevVersion(); } return true; } private identifierOrParameter_sempred(localctx: IdentifierOrParameterContext, predIndex: number): boolean { switch (predIndex) { - case 12: + case 10: return this.isDevVersion(); } return true; } - public static readonly _serializedATN: number[] = [4,1,130,651,2,0,7,0, + public static readonly _serializedATN: number[] = [4,1,130,644,2,0,7,0, 2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9, 2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2, 17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24, @@ -3714,207 +3673,204 @@ export default class esql_parser extends parser_config { 60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,65,7,65,1,0,1,0,1,0,1,1,1, 1,1,1,1,1,1,1,1,1,5,1,142,8,1,10,1,12,1,145,9,1,1,2,1,2,1,2,1,2,1,2,1,2, 3,2,153,8,2,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3, - 1,3,1,3,1,3,3,3,173,8,3,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,185, - 8,5,1,5,1,5,1,5,1,5,1,5,5,5,192,8,5,10,5,12,5,195,9,5,1,5,1,5,1,5,1,5,1, - 5,3,5,202,8,5,1,5,1,5,1,5,3,5,207,8,5,1,5,1,5,1,5,1,5,1,5,1,5,5,5,215,8, - 5,10,5,12,5,218,9,5,1,6,1,6,3,6,222,8,6,1,6,1,6,1,6,1,6,1,6,3,6,229,8,6, - 1,6,1,6,1,6,3,6,234,8,6,1,7,1,7,1,7,3,7,239,8,7,1,7,1,7,1,7,1,8,1,8,1,8, - 1,8,1,8,3,8,249,8,8,1,9,1,9,1,9,1,9,3,9,255,8,9,1,9,1,9,1,9,1,9,1,9,1,9, - 5,9,263,8,9,10,9,12,9,266,9,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,3, - 10,276,8,10,1,10,1,10,1,10,5,10,281,8,10,10,10,12,10,284,9,10,1,11,1,11, - 1,11,1,11,1,11,1,11,5,11,292,8,11,10,11,12,11,295,9,11,1,11,1,11,3,11,299, - 8,11,3,11,301,8,11,1,11,1,11,1,12,1,12,1,13,1,13,1,13,1,13,1,13,5,13,312, - 8,13,10,13,12,13,315,9,13,1,13,1,13,1,14,1,14,1,14,1,14,1,15,1,15,1,16, - 1,16,1,16,1,17,1,17,1,17,5,17,331,8,17,10,17,12,17,334,9,17,1,18,1,18,1, - 18,3,18,339,8,18,1,18,1,18,1,19,1,19,1,19,1,19,5,19,347,8,19,10,19,12,19, - 350,9,19,1,19,3,19,353,8,19,1,20,1,20,1,20,3,20,358,8,20,1,20,1,20,1,21, - 1,21,1,22,1,22,1,23,1,23,1,23,1,23,5,23,370,8,23,10,23,12,23,373,9,23,1, - 24,1,24,1,24,1,24,5,24,379,8,24,10,24,12,24,382,9,24,1,24,3,24,385,8,24, - 1,24,1,24,3,24,389,8,24,1,25,1,25,1,25,1,26,1,26,3,26,396,8,26,1,26,1,26, - 3,26,400,8,26,1,27,1,27,1,27,5,27,405,8,27,10,27,12,27,408,9,27,1,28,1, - 28,1,28,3,28,413,8,28,1,29,1,29,1,29,5,29,418,8,29,10,29,12,29,421,9,29, - 1,30,1,30,1,30,5,30,426,8,30,10,30,12,30,429,9,30,1,31,1,31,1,31,5,31,434, - 8,31,10,31,12,31,437,9,31,1,32,1,32,1,33,1,33,1,33,3,33,444,8,33,1,34,1, - 34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,459,8,34, - 10,34,12,34,462,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,470,8,34,10,34, - 12,34,473,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,481,8,34,10,34,12,34, - 484,9,34,1,34,1,34,3,34,488,8,34,1,35,1,35,3,35,492,8,35,1,36,1,36,1,36, - 3,36,497,8,36,1,37,1,37,1,37,1,38,1,38,1,38,1,38,5,38,506,8,38,10,38,12, - 38,509,9,38,1,39,1,39,3,39,513,8,39,1,39,1,39,3,39,517,8,39,1,40,1,40,1, - 40,1,41,1,41,1,41,1,42,1,42,1,42,1,42,5,42,529,8,42,10,42,12,42,532,9,42, - 1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,3,44,542,8,44,1,45,1,45,1,45,1, - 45,1,46,1,46,1,46,1,47,1,47,1,47,5,47,554,8,47,10,47,12,47,557,9,47,1,48, - 1,48,1,48,1,48,1,49,1,49,1,50,1,50,3,50,567,8,50,1,51,3,51,570,8,51,1,51, - 1,51,1,52,3,52,575,8,52,1,52,1,52,1,53,1,53,1,54,1,54,1,55,1,55,1,55,1, - 56,1,56,1,56,1,56,1,57,1,57,1,57,1,58,1,58,1,58,1,58,3,58,597,8,58,1,58, - 1,58,1,58,1,58,5,58,603,8,58,10,58,12,58,606,9,58,3,58,608,8,58,1,59,1, - 59,1,59,3,59,613,8,59,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61, - 1,61,3,61,626,8,61,1,62,3,62,629,8,62,1,62,1,62,1,62,1,62,1,63,1,63,1,63, - 3,63,638,8,63,1,64,1,64,1,64,1,64,5,64,644,8,64,10,64,12,64,647,9,64,1, - 65,1,65,1,65,0,4,2,10,18,20,66,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28, - 30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76, - 78,80,82,84,86,88,90,92,94,96,98,100,102,104,106,108,110,112,114,116,118, - 120,122,124,126,128,130,0,9,1,0,64,65,1,0,66,68,2,0,30,30,83,83,1,0,74, - 75,2,0,35,35,40,40,2,0,43,43,46,46,2,0,42,42,56,56,2,0,57,57,59,63,1,0, - 22,24,678,0,132,1,0,0,0,2,135,1,0,0,0,4,152,1,0,0,0,6,172,1,0,0,0,8,174, - 1,0,0,0,10,206,1,0,0,0,12,233,1,0,0,0,14,235,1,0,0,0,16,248,1,0,0,0,18, - 254,1,0,0,0,20,275,1,0,0,0,22,285,1,0,0,0,24,304,1,0,0,0,26,306,1,0,0,0, - 28,318,1,0,0,0,30,322,1,0,0,0,32,324,1,0,0,0,34,327,1,0,0,0,36,338,1,0, - 0,0,38,342,1,0,0,0,40,357,1,0,0,0,42,361,1,0,0,0,44,363,1,0,0,0,46,365, - 1,0,0,0,48,374,1,0,0,0,50,390,1,0,0,0,52,393,1,0,0,0,54,401,1,0,0,0,56, - 409,1,0,0,0,58,414,1,0,0,0,60,422,1,0,0,0,62,430,1,0,0,0,64,438,1,0,0,0, - 66,443,1,0,0,0,68,487,1,0,0,0,70,491,1,0,0,0,72,496,1,0,0,0,74,498,1,0, - 0,0,76,501,1,0,0,0,78,510,1,0,0,0,80,518,1,0,0,0,82,521,1,0,0,0,84,524, - 1,0,0,0,86,533,1,0,0,0,88,537,1,0,0,0,90,543,1,0,0,0,92,547,1,0,0,0,94, - 550,1,0,0,0,96,558,1,0,0,0,98,562,1,0,0,0,100,566,1,0,0,0,102,569,1,0,0, - 0,104,574,1,0,0,0,106,578,1,0,0,0,108,580,1,0,0,0,110,582,1,0,0,0,112,585, - 1,0,0,0,114,589,1,0,0,0,116,592,1,0,0,0,118,612,1,0,0,0,120,616,1,0,0,0, - 122,621,1,0,0,0,124,628,1,0,0,0,126,634,1,0,0,0,128,639,1,0,0,0,130,648, - 1,0,0,0,132,133,3,2,1,0,133,134,5,0,0,1,134,1,1,0,0,0,135,136,6,1,-1,0, - 136,137,3,4,2,0,137,143,1,0,0,0,138,139,10,1,0,0,139,140,5,29,0,0,140,142, - 3,6,3,0,141,138,1,0,0,0,142,145,1,0,0,0,143,141,1,0,0,0,143,144,1,0,0,0, - 144,3,1,0,0,0,145,143,1,0,0,0,146,153,3,110,55,0,147,153,3,38,19,0,148, - 153,3,32,16,0,149,153,3,114,57,0,150,151,4,2,1,0,151,153,3,48,24,0,152, - 146,1,0,0,0,152,147,1,0,0,0,152,148,1,0,0,0,152,149,1,0,0,0,152,150,1,0, - 0,0,153,5,1,0,0,0,154,173,3,50,25,0,155,173,3,8,4,0,156,173,3,80,40,0,157, - 173,3,74,37,0,158,173,3,52,26,0,159,173,3,76,38,0,160,173,3,82,41,0,161, - 173,3,84,42,0,162,173,3,88,44,0,163,173,3,90,45,0,164,173,3,116,58,0,165, - 173,3,92,46,0,166,167,4,3,2,0,167,173,3,122,61,0,168,169,4,3,3,0,169,173, - 3,120,60,0,170,171,4,3,4,0,171,173,3,124,62,0,172,154,1,0,0,0,172,155,1, - 0,0,0,172,156,1,0,0,0,172,157,1,0,0,0,172,158,1,0,0,0,172,159,1,0,0,0,172, - 160,1,0,0,0,172,161,1,0,0,0,172,162,1,0,0,0,172,163,1,0,0,0,172,164,1,0, - 0,0,172,165,1,0,0,0,172,166,1,0,0,0,172,168,1,0,0,0,172,170,1,0,0,0,173, - 7,1,0,0,0,174,175,5,16,0,0,175,176,3,10,5,0,176,9,1,0,0,0,177,178,6,5,-1, - 0,178,179,5,49,0,0,179,207,3,10,5,8,180,207,3,16,8,0,181,207,3,12,6,0,182, - 184,3,16,8,0,183,185,5,49,0,0,184,183,1,0,0,0,184,185,1,0,0,0,185,186,1, - 0,0,0,186,187,5,44,0,0,187,188,5,48,0,0,188,193,3,16,8,0,189,190,5,39,0, - 0,190,192,3,16,8,0,191,189,1,0,0,0,192,195,1,0,0,0,193,191,1,0,0,0,193, - 194,1,0,0,0,194,196,1,0,0,0,195,193,1,0,0,0,196,197,5,55,0,0,197,207,1, - 0,0,0,198,199,3,16,8,0,199,201,5,45,0,0,200,202,5,49,0,0,201,200,1,0,0, - 0,201,202,1,0,0,0,202,203,1,0,0,0,203,204,5,50,0,0,204,207,1,0,0,0,205, - 207,3,14,7,0,206,177,1,0,0,0,206,180,1,0,0,0,206,181,1,0,0,0,206,182,1, - 0,0,0,206,198,1,0,0,0,206,205,1,0,0,0,207,216,1,0,0,0,208,209,10,5,0,0, - 209,210,5,34,0,0,210,215,3,10,5,6,211,212,10,4,0,0,212,213,5,52,0,0,213, - 215,3,10,5,5,214,208,1,0,0,0,214,211,1,0,0,0,215,218,1,0,0,0,216,214,1, - 0,0,0,216,217,1,0,0,0,217,11,1,0,0,0,218,216,1,0,0,0,219,221,3,16,8,0,220, - 222,5,49,0,0,221,220,1,0,0,0,221,222,1,0,0,0,222,223,1,0,0,0,223,224,5, - 47,0,0,224,225,3,106,53,0,225,234,1,0,0,0,226,228,3,16,8,0,227,229,5,49, - 0,0,228,227,1,0,0,0,228,229,1,0,0,0,229,230,1,0,0,0,230,231,5,54,0,0,231, - 232,3,106,53,0,232,234,1,0,0,0,233,219,1,0,0,0,233,226,1,0,0,0,234,13,1, - 0,0,0,235,238,3,58,29,0,236,237,5,37,0,0,237,239,3,30,15,0,238,236,1,0, - 0,0,238,239,1,0,0,0,239,240,1,0,0,0,240,241,5,38,0,0,241,242,3,68,34,0, - 242,15,1,0,0,0,243,249,3,18,9,0,244,245,3,18,9,0,245,246,3,108,54,0,246, - 247,3,18,9,0,247,249,1,0,0,0,248,243,1,0,0,0,248,244,1,0,0,0,249,17,1,0, - 0,0,250,251,6,9,-1,0,251,255,3,20,10,0,252,253,7,0,0,0,253,255,3,18,9,3, - 254,250,1,0,0,0,254,252,1,0,0,0,255,264,1,0,0,0,256,257,10,2,0,0,257,258, - 7,1,0,0,258,263,3,18,9,3,259,260,10,1,0,0,260,261,7,0,0,0,261,263,3,18, - 9,2,262,256,1,0,0,0,262,259,1,0,0,0,263,266,1,0,0,0,264,262,1,0,0,0,264, - 265,1,0,0,0,265,19,1,0,0,0,266,264,1,0,0,0,267,268,6,10,-1,0,268,276,3, - 68,34,0,269,276,3,58,29,0,270,276,3,22,11,0,271,272,5,48,0,0,272,273,3, - 10,5,0,273,274,5,55,0,0,274,276,1,0,0,0,275,267,1,0,0,0,275,269,1,0,0,0, - 275,270,1,0,0,0,275,271,1,0,0,0,276,282,1,0,0,0,277,278,10,1,0,0,278,279, - 5,37,0,0,279,281,3,30,15,0,280,277,1,0,0,0,281,284,1,0,0,0,282,280,1,0, - 0,0,282,283,1,0,0,0,283,21,1,0,0,0,284,282,1,0,0,0,285,286,3,24,12,0,286, - 300,5,48,0,0,287,301,5,66,0,0,288,293,3,10,5,0,289,290,5,39,0,0,290,292, - 3,10,5,0,291,289,1,0,0,0,292,295,1,0,0,0,293,291,1,0,0,0,293,294,1,0,0, - 0,294,298,1,0,0,0,295,293,1,0,0,0,296,297,5,39,0,0,297,299,3,26,13,0,298, - 296,1,0,0,0,298,299,1,0,0,0,299,301,1,0,0,0,300,287,1,0,0,0,300,288,1,0, - 0,0,300,301,1,0,0,0,301,302,1,0,0,0,302,303,5,55,0,0,303,23,1,0,0,0,304, - 305,3,72,36,0,305,25,1,0,0,0,306,307,4,13,10,0,307,308,5,69,0,0,308,313, - 3,28,14,0,309,310,5,39,0,0,310,312,3,28,14,0,311,309,1,0,0,0,312,315,1, - 0,0,0,313,311,1,0,0,0,313,314,1,0,0,0,314,316,1,0,0,0,315,313,1,0,0,0,316, - 317,5,70,0,0,317,27,1,0,0,0,318,319,3,106,53,0,319,320,5,38,0,0,320,321, - 3,68,34,0,321,29,1,0,0,0,322,323,3,64,32,0,323,31,1,0,0,0,324,325,5,12, - 0,0,325,326,3,34,17,0,326,33,1,0,0,0,327,332,3,36,18,0,328,329,5,39,0,0, - 329,331,3,36,18,0,330,328,1,0,0,0,331,334,1,0,0,0,332,330,1,0,0,0,332,333, - 1,0,0,0,333,35,1,0,0,0,334,332,1,0,0,0,335,336,3,58,29,0,336,337,5,36,0, - 0,337,339,1,0,0,0,338,335,1,0,0,0,338,339,1,0,0,0,339,340,1,0,0,0,340,341, - 3,10,5,0,341,37,1,0,0,0,342,343,5,6,0,0,343,348,3,40,20,0,344,345,5,39, - 0,0,345,347,3,40,20,0,346,344,1,0,0,0,347,350,1,0,0,0,348,346,1,0,0,0,348, - 349,1,0,0,0,349,352,1,0,0,0,350,348,1,0,0,0,351,353,3,46,23,0,352,351,1, - 0,0,0,352,353,1,0,0,0,353,39,1,0,0,0,354,355,3,42,21,0,355,356,5,38,0,0, - 356,358,1,0,0,0,357,354,1,0,0,0,357,358,1,0,0,0,358,359,1,0,0,0,359,360, - 3,44,22,0,360,41,1,0,0,0,361,362,5,83,0,0,362,43,1,0,0,0,363,364,7,2,0, - 0,364,45,1,0,0,0,365,366,5,82,0,0,366,371,5,83,0,0,367,368,5,39,0,0,368, - 370,5,83,0,0,369,367,1,0,0,0,370,373,1,0,0,0,371,369,1,0,0,0,371,372,1, - 0,0,0,372,47,1,0,0,0,373,371,1,0,0,0,374,375,5,19,0,0,375,380,3,40,20,0, - 376,377,5,39,0,0,377,379,3,40,20,0,378,376,1,0,0,0,379,382,1,0,0,0,380, - 378,1,0,0,0,380,381,1,0,0,0,381,384,1,0,0,0,382,380,1,0,0,0,383,385,3,54, - 27,0,384,383,1,0,0,0,384,385,1,0,0,0,385,388,1,0,0,0,386,387,5,33,0,0,387, - 389,3,34,17,0,388,386,1,0,0,0,388,389,1,0,0,0,389,49,1,0,0,0,390,391,5, - 4,0,0,391,392,3,34,17,0,392,51,1,0,0,0,393,395,5,15,0,0,394,396,3,54,27, - 0,395,394,1,0,0,0,395,396,1,0,0,0,396,399,1,0,0,0,397,398,5,33,0,0,398, - 400,3,34,17,0,399,397,1,0,0,0,399,400,1,0,0,0,400,53,1,0,0,0,401,406,3, - 56,28,0,402,403,5,39,0,0,403,405,3,56,28,0,404,402,1,0,0,0,405,408,1,0, - 0,0,406,404,1,0,0,0,406,407,1,0,0,0,407,55,1,0,0,0,408,406,1,0,0,0,409, - 412,3,36,18,0,410,411,5,16,0,0,411,413,3,10,5,0,412,410,1,0,0,0,412,413, - 1,0,0,0,413,57,1,0,0,0,414,419,3,72,36,0,415,416,5,41,0,0,416,418,3,72, - 36,0,417,415,1,0,0,0,418,421,1,0,0,0,419,417,1,0,0,0,419,420,1,0,0,0,420, - 59,1,0,0,0,421,419,1,0,0,0,422,427,3,66,33,0,423,424,5,41,0,0,424,426,3, - 66,33,0,425,423,1,0,0,0,426,429,1,0,0,0,427,425,1,0,0,0,427,428,1,0,0,0, - 428,61,1,0,0,0,429,427,1,0,0,0,430,435,3,60,30,0,431,432,5,39,0,0,432,434, - 3,60,30,0,433,431,1,0,0,0,434,437,1,0,0,0,435,433,1,0,0,0,435,436,1,0,0, - 0,436,63,1,0,0,0,437,435,1,0,0,0,438,439,7,3,0,0,439,65,1,0,0,0,440,444, - 5,87,0,0,441,442,4,33,11,0,442,444,3,70,35,0,443,440,1,0,0,0,443,441,1, - 0,0,0,444,67,1,0,0,0,445,488,5,50,0,0,446,447,3,104,52,0,447,448,5,74,0, - 0,448,488,1,0,0,0,449,488,3,102,51,0,450,488,3,104,52,0,451,488,3,98,49, - 0,452,488,3,70,35,0,453,488,3,106,53,0,454,455,5,72,0,0,455,460,3,100,50, - 0,456,457,5,39,0,0,457,459,3,100,50,0,458,456,1,0,0,0,459,462,1,0,0,0,460, - 458,1,0,0,0,460,461,1,0,0,0,461,463,1,0,0,0,462,460,1,0,0,0,463,464,5,73, - 0,0,464,488,1,0,0,0,465,466,5,72,0,0,466,471,3,98,49,0,467,468,5,39,0,0, - 468,470,3,98,49,0,469,467,1,0,0,0,470,473,1,0,0,0,471,469,1,0,0,0,471,472, - 1,0,0,0,472,474,1,0,0,0,473,471,1,0,0,0,474,475,5,73,0,0,475,488,1,0,0, - 0,476,477,5,72,0,0,477,482,3,106,53,0,478,479,5,39,0,0,479,481,3,106,53, - 0,480,478,1,0,0,0,481,484,1,0,0,0,482,480,1,0,0,0,482,483,1,0,0,0,483,485, - 1,0,0,0,484,482,1,0,0,0,485,486,5,73,0,0,486,488,1,0,0,0,487,445,1,0,0, - 0,487,446,1,0,0,0,487,449,1,0,0,0,487,450,1,0,0,0,487,451,1,0,0,0,487,452, - 1,0,0,0,487,453,1,0,0,0,487,454,1,0,0,0,487,465,1,0,0,0,487,476,1,0,0,0, - 488,69,1,0,0,0,489,492,5,53,0,0,490,492,5,71,0,0,491,489,1,0,0,0,491,490, - 1,0,0,0,492,71,1,0,0,0,493,497,3,64,32,0,494,495,4,36,12,0,495,497,3,70, - 35,0,496,493,1,0,0,0,496,494,1,0,0,0,497,73,1,0,0,0,498,499,5,9,0,0,499, - 500,5,31,0,0,500,75,1,0,0,0,501,502,5,14,0,0,502,507,3,78,39,0,503,504, - 5,39,0,0,504,506,3,78,39,0,505,503,1,0,0,0,506,509,1,0,0,0,507,505,1,0, - 0,0,507,508,1,0,0,0,508,77,1,0,0,0,509,507,1,0,0,0,510,512,3,10,5,0,511, - 513,7,4,0,0,512,511,1,0,0,0,512,513,1,0,0,0,513,516,1,0,0,0,514,515,5,51, - 0,0,515,517,7,5,0,0,516,514,1,0,0,0,516,517,1,0,0,0,517,79,1,0,0,0,518, - 519,5,8,0,0,519,520,3,62,31,0,520,81,1,0,0,0,521,522,5,2,0,0,522,523,3, - 62,31,0,523,83,1,0,0,0,524,525,5,11,0,0,525,530,3,86,43,0,526,527,5,39, - 0,0,527,529,3,86,43,0,528,526,1,0,0,0,529,532,1,0,0,0,530,528,1,0,0,0,530, - 531,1,0,0,0,531,85,1,0,0,0,532,530,1,0,0,0,533,534,3,60,30,0,534,535,5, - 91,0,0,535,536,3,60,30,0,536,87,1,0,0,0,537,538,5,1,0,0,538,539,3,20,10, - 0,539,541,3,106,53,0,540,542,3,94,47,0,541,540,1,0,0,0,541,542,1,0,0,0, - 542,89,1,0,0,0,543,544,5,7,0,0,544,545,3,20,10,0,545,546,3,106,53,0,546, - 91,1,0,0,0,547,548,5,10,0,0,548,549,3,58,29,0,549,93,1,0,0,0,550,555,3, - 96,48,0,551,552,5,39,0,0,552,554,3,96,48,0,553,551,1,0,0,0,554,557,1,0, - 0,0,555,553,1,0,0,0,555,556,1,0,0,0,556,95,1,0,0,0,557,555,1,0,0,0,558, - 559,3,64,32,0,559,560,5,36,0,0,560,561,3,68,34,0,561,97,1,0,0,0,562,563, - 7,6,0,0,563,99,1,0,0,0,564,567,3,102,51,0,565,567,3,104,52,0,566,564,1, - 0,0,0,566,565,1,0,0,0,567,101,1,0,0,0,568,570,7,0,0,0,569,568,1,0,0,0,569, - 570,1,0,0,0,570,571,1,0,0,0,571,572,5,32,0,0,572,103,1,0,0,0,573,575,7, - 0,0,0,574,573,1,0,0,0,574,575,1,0,0,0,575,576,1,0,0,0,576,577,5,31,0,0, - 577,105,1,0,0,0,578,579,5,30,0,0,579,107,1,0,0,0,580,581,7,7,0,0,581,109, - 1,0,0,0,582,583,5,5,0,0,583,584,3,112,56,0,584,111,1,0,0,0,585,586,5,72, - 0,0,586,587,3,2,1,0,587,588,5,73,0,0,588,113,1,0,0,0,589,590,5,13,0,0,590, - 591,5,107,0,0,591,115,1,0,0,0,592,593,5,3,0,0,593,596,5,97,0,0,594,595, - 5,95,0,0,595,597,3,60,30,0,596,594,1,0,0,0,596,597,1,0,0,0,597,607,1,0, - 0,0,598,599,5,96,0,0,599,604,3,118,59,0,600,601,5,39,0,0,601,603,3,118, - 59,0,602,600,1,0,0,0,603,606,1,0,0,0,604,602,1,0,0,0,604,605,1,0,0,0,605, - 608,1,0,0,0,606,604,1,0,0,0,607,598,1,0,0,0,607,608,1,0,0,0,608,117,1,0, - 0,0,609,610,3,60,30,0,610,611,5,36,0,0,611,613,1,0,0,0,612,609,1,0,0,0, - 612,613,1,0,0,0,613,614,1,0,0,0,614,615,3,60,30,0,615,119,1,0,0,0,616,617, - 5,18,0,0,617,618,3,40,20,0,618,619,5,95,0,0,619,620,3,62,31,0,620,121,1, - 0,0,0,621,622,5,17,0,0,622,625,3,54,27,0,623,624,5,33,0,0,624,626,3,34, - 17,0,625,623,1,0,0,0,625,626,1,0,0,0,626,123,1,0,0,0,627,629,7,8,0,0,628, - 627,1,0,0,0,628,629,1,0,0,0,629,630,1,0,0,0,630,631,5,20,0,0,631,632,3, - 126,63,0,632,633,3,128,64,0,633,125,1,0,0,0,634,637,3,40,20,0,635,636,5, - 91,0,0,636,638,3,64,32,0,637,635,1,0,0,0,637,638,1,0,0,0,638,127,1,0,0, - 0,639,640,5,95,0,0,640,645,3,130,65,0,641,642,5,39,0,0,642,644,3,130,65, - 0,643,641,1,0,0,0,644,647,1,0,0,0,645,643,1,0,0,0,645,646,1,0,0,0,646,129, - 1,0,0,0,647,645,1,0,0,0,648,649,3,16,8,0,649,131,1,0,0,0,63,143,152,172, - 184,193,201,206,214,216,221,228,233,238,248,254,262,264,275,282,293,298, - 300,313,332,338,348,352,357,371,380,384,388,395,399,406,412,419,427,435, - 443,460,471,482,487,491,496,507,512,516,530,541,555,566,569,574,596,604, - 607,612,625,628,637,645]; + 1,3,1,3,3,3,172,8,3,1,4,1,4,1,4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,184,8,5, + 1,5,1,5,1,5,1,5,1,5,5,5,191,8,5,10,5,12,5,194,9,5,1,5,1,5,1,5,1,5,1,5,3, + 5,201,8,5,1,5,1,5,1,5,3,5,206,8,5,1,5,1,5,1,5,1,5,1,5,1,5,5,5,214,8,5,10, + 5,12,5,217,9,5,1,6,1,6,3,6,221,8,6,1,6,1,6,1,6,1,6,1,6,3,6,228,8,6,1,6, + 1,6,1,6,3,6,233,8,6,1,7,1,7,1,7,3,7,238,8,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8, + 1,8,3,8,248,8,8,1,9,1,9,1,9,1,9,3,9,254,8,9,1,9,1,9,1,9,1,9,1,9,1,9,5,9, + 262,8,9,10,9,12,9,265,9,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,3,10, + 275,8,10,1,10,1,10,1,10,5,10,280,8,10,10,10,12,10,283,9,10,1,11,1,11,1, + 11,1,11,1,11,1,11,5,11,291,8,11,10,11,12,11,294,9,11,1,11,1,11,3,11,298, + 8,11,3,11,300,8,11,1,11,1,11,1,12,1,12,1,13,1,13,1,13,1,13,5,13,310,8,13, + 10,13,12,13,313,9,13,1,13,1,13,1,14,1,14,1,14,1,14,1,15,1,15,1,16,1,16, + 1,16,1,17,1,17,1,17,5,17,329,8,17,10,17,12,17,332,9,17,1,18,1,18,1,18,3, + 18,337,8,18,1,18,1,18,1,19,1,19,1,19,1,19,5,19,345,8,19,10,19,12,19,348, + 9,19,1,19,3,19,351,8,19,1,20,1,20,1,20,3,20,356,8,20,1,20,1,20,1,21,1,21, + 1,22,1,22,1,23,1,23,1,23,1,23,5,23,368,8,23,10,23,12,23,371,9,23,1,24,1, + 24,1,24,1,24,5,24,377,8,24,10,24,12,24,380,9,24,1,24,3,24,383,8,24,1,24, + 1,24,3,24,387,8,24,1,25,1,25,1,25,1,26,1,26,3,26,394,8,26,1,26,1,26,3,26, + 398,8,26,1,27,1,27,1,27,5,27,403,8,27,10,27,12,27,406,9,27,1,28,1,28,1, + 28,3,28,411,8,28,1,29,1,29,1,29,5,29,416,8,29,10,29,12,29,419,9,29,1,30, + 1,30,1,30,5,30,424,8,30,10,30,12,30,427,9,30,1,31,1,31,1,31,5,31,432,8, + 31,10,31,12,31,435,9,31,1,32,1,32,1,33,1,33,1,33,3,33,442,8,33,1,34,1,34, + 1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,457,8,34,10, + 34,12,34,460,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,468,8,34,10,34,12, + 34,471,9,34,1,34,1,34,1,34,1,34,1,34,1,34,5,34,479,8,34,10,34,12,34,482, + 9,34,1,34,1,34,3,34,486,8,34,1,35,1,35,3,35,490,8,35,1,36,1,36,1,36,3,36, + 495,8,36,1,37,1,37,1,37,1,38,1,38,1,38,1,38,5,38,504,8,38,10,38,12,38,507, + 9,38,1,39,1,39,3,39,511,8,39,1,39,1,39,3,39,515,8,39,1,40,1,40,1,40,1,41, + 1,41,1,41,1,42,1,42,1,42,1,42,5,42,527,8,42,10,42,12,42,530,9,42,1,43,1, + 43,1,43,1,43,1,44,1,44,1,44,1,44,3,44,540,8,44,1,45,1,45,1,45,1,45,1,46, + 1,46,1,46,1,47,1,47,1,47,5,47,552,8,47,10,47,12,47,555,9,47,1,48,1,48,1, + 48,1,48,1,49,1,49,1,50,1,50,3,50,565,8,50,1,51,3,51,568,8,51,1,51,1,51, + 1,52,3,52,573,8,52,1,52,1,52,1,53,1,53,1,54,1,54,1,55,1,55,1,55,1,56,1, + 56,1,56,1,56,1,57,1,57,1,57,1,58,1,58,1,58,1,58,3,58,595,8,58,1,58,1,58, + 1,58,1,58,5,58,601,8,58,10,58,12,58,604,9,58,3,58,606,8,58,1,59,1,59,1, + 59,3,59,611,8,59,1,59,1,59,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61, + 3,61,624,8,61,1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,64,1,64,1,64,1,64,5, + 64,637,8,64,10,64,12,64,640,9,64,1,65,1,65,1,65,0,4,2,10,18,20,66,0,2,4, + 6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54, + 56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,96,98,100,102, + 104,106,108,110,112,114,116,118,120,122,124,126,128,130,0,9,1,0,63,64,1, + 0,65,67,2,0,29,29,82,82,1,0,73,74,2,0,34,34,39,39,2,0,42,42,45,45,2,0,41, + 41,55,55,2,0,56,56,58,62,2,0,17,17,22,23,669,0,132,1,0,0,0,2,135,1,0,0, + 0,4,152,1,0,0,0,6,171,1,0,0,0,8,173,1,0,0,0,10,205,1,0,0,0,12,232,1,0,0, + 0,14,234,1,0,0,0,16,247,1,0,0,0,18,253,1,0,0,0,20,274,1,0,0,0,22,284,1, + 0,0,0,24,303,1,0,0,0,26,305,1,0,0,0,28,316,1,0,0,0,30,320,1,0,0,0,32,322, + 1,0,0,0,34,325,1,0,0,0,36,336,1,0,0,0,38,340,1,0,0,0,40,355,1,0,0,0,42, + 359,1,0,0,0,44,361,1,0,0,0,46,363,1,0,0,0,48,372,1,0,0,0,50,388,1,0,0,0, + 52,391,1,0,0,0,54,399,1,0,0,0,56,407,1,0,0,0,58,412,1,0,0,0,60,420,1,0, + 0,0,62,428,1,0,0,0,64,436,1,0,0,0,66,441,1,0,0,0,68,485,1,0,0,0,70,489, + 1,0,0,0,72,494,1,0,0,0,74,496,1,0,0,0,76,499,1,0,0,0,78,508,1,0,0,0,80, + 516,1,0,0,0,82,519,1,0,0,0,84,522,1,0,0,0,86,531,1,0,0,0,88,535,1,0,0,0, + 90,541,1,0,0,0,92,545,1,0,0,0,94,548,1,0,0,0,96,556,1,0,0,0,98,560,1,0, + 0,0,100,564,1,0,0,0,102,567,1,0,0,0,104,572,1,0,0,0,106,576,1,0,0,0,108, + 578,1,0,0,0,110,580,1,0,0,0,112,583,1,0,0,0,114,587,1,0,0,0,116,590,1,0, + 0,0,118,610,1,0,0,0,120,614,1,0,0,0,122,619,1,0,0,0,124,625,1,0,0,0,126, + 630,1,0,0,0,128,632,1,0,0,0,130,641,1,0,0,0,132,133,3,2,1,0,133,134,5,0, + 0,1,134,1,1,0,0,0,135,136,6,1,-1,0,136,137,3,4,2,0,137,143,1,0,0,0,138, + 139,10,1,0,0,139,140,5,28,0,0,140,142,3,6,3,0,141,138,1,0,0,0,142,145,1, + 0,0,0,143,141,1,0,0,0,143,144,1,0,0,0,144,3,1,0,0,0,145,143,1,0,0,0,146, + 153,3,110,55,0,147,153,3,38,19,0,148,153,3,32,16,0,149,153,3,114,57,0,150, + 151,4,2,1,0,151,153,3,48,24,0,152,146,1,0,0,0,152,147,1,0,0,0,152,148,1, + 0,0,0,152,149,1,0,0,0,152,150,1,0,0,0,153,5,1,0,0,0,154,172,3,50,25,0,155, + 172,3,8,4,0,156,172,3,80,40,0,157,172,3,74,37,0,158,172,3,52,26,0,159,172, + 3,76,38,0,160,172,3,82,41,0,161,172,3,84,42,0,162,172,3,88,44,0,163,172, + 3,90,45,0,164,172,3,116,58,0,165,172,3,92,46,0,166,172,3,124,62,0,167,168, + 4,3,2,0,168,172,3,122,61,0,169,170,4,3,3,0,170,172,3,120,60,0,171,154,1, + 0,0,0,171,155,1,0,0,0,171,156,1,0,0,0,171,157,1,0,0,0,171,158,1,0,0,0,171, + 159,1,0,0,0,171,160,1,0,0,0,171,161,1,0,0,0,171,162,1,0,0,0,171,163,1,0, + 0,0,171,164,1,0,0,0,171,165,1,0,0,0,171,166,1,0,0,0,171,167,1,0,0,0,171, + 169,1,0,0,0,172,7,1,0,0,0,173,174,5,16,0,0,174,175,3,10,5,0,175,9,1,0,0, + 0,176,177,6,5,-1,0,177,178,5,48,0,0,178,206,3,10,5,8,179,206,3,16,8,0,180, + 206,3,12,6,0,181,183,3,16,8,0,182,184,5,48,0,0,183,182,1,0,0,0,183,184, + 1,0,0,0,184,185,1,0,0,0,185,186,5,43,0,0,186,187,5,47,0,0,187,192,3,16, + 8,0,188,189,5,38,0,0,189,191,3,16,8,0,190,188,1,0,0,0,191,194,1,0,0,0,192, + 190,1,0,0,0,192,193,1,0,0,0,193,195,1,0,0,0,194,192,1,0,0,0,195,196,5,54, + 0,0,196,206,1,0,0,0,197,198,3,16,8,0,198,200,5,44,0,0,199,201,5,48,0,0, + 200,199,1,0,0,0,200,201,1,0,0,0,201,202,1,0,0,0,202,203,5,49,0,0,203,206, + 1,0,0,0,204,206,3,14,7,0,205,176,1,0,0,0,205,179,1,0,0,0,205,180,1,0,0, + 0,205,181,1,0,0,0,205,197,1,0,0,0,205,204,1,0,0,0,206,215,1,0,0,0,207,208, + 10,5,0,0,208,209,5,33,0,0,209,214,3,10,5,6,210,211,10,4,0,0,211,212,5,51, + 0,0,212,214,3,10,5,5,213,207,1,0,0,0,213,210,1,0,0,0,214,217,1,0,0,0,215, + 213,1,0,0,0,215,216,1,0,0,0,216,11,1,0,0,0,217,215,1,0,0,0,218,220,3,16, + 8,0,219,221,5,48,0,0,220,219,1,0,0,0,220,221,1,0,0,0,221,222,1,0,0,0,222, + 223,5,46,0,0,223,224,3,106,53,0,224,233,1,0,0,0,225,227,3,16,8,0,226,228, + 5,48,0,0,227,226,1,0,0,0,227,228,1,0,0,0,228,229,1,0,0,0,229,230,5,53,0, + 0,230,231,3,106,53,0,231,233,1,0,0,0,232,218,1,0,0,0,232,225,1,0,0,0,233, + 13,1,0,0,0,234,237,3,58,29,0,235,236,5,36,0,0,236,238,3,30,15,0,237,235, + 1,0,0,0,237,238,1,0,0,0,238,239,1,0,0,0,239,240,5,37,0,0,240,241,3,68,34, + 0,241,15,1,0,0,0,242,248,3,18,9,0,243,244,3,18,9,0,244,245,3,108,54,0,245, + 246,3,18,9,0,246,248,1,0,0,0,247,242,1,0,0,0,247,243,1,0,0,0,248,17,1,0, + 0,0,249,250,6,9,-1,0,250,254,3,20,10,0,251,252,7,0,0,0,252,254,3,18,9,3, + 253,249,1,0,0,0,253,251,1,0,0,0,254,263,1,0,0,0,255,256,10,2,0,0,256,257, + 7,1,0,0,257,262,3,18,9,3,258,259,10,1,0,0,259,260,7,0,0,0,260,262,3,18, + 9,2,261,255,1,0,0,0,261,258,1,0,0,0,262,265,1,0,0,0,263,261,1,0,0,0,263, + 264,1,0,0,0,264,19,1,0,0,0,265,263,1,0,0,0,266,267,6,10,-1,0,267,275,3, + 68,34,0,268,275,3,58,29,0,269,275,3,22,11,0,270,271,5,47,0,0,271,272,3, + 10,5,0,272,273,5,54,0,0,273,275,1,0,0,0,274,266,1,0,0,0,274,268,1,0,0,0, + 274,269,1,0,0,0,274,270,1,0,0,0,275,281,1,0,0,0,276,277,10,1,0,0,277,278, + 5,36,0,0,278,280,3,30,15,0,279,276,1,0,0,0,280,283,1,0,0,0,281,279,1,0, + 0,0,281,282,1,0,0,0,282,21,1,0,0,0,283,281,1,0,0,0,284,285,3,24,12,0,285, + 299,5,47,0,0,286,300,5,65,0,0,287,292,3,10,5,0,288,289,5,38,0,0,289,291, + 3,10,5,0,290,288,1,0,0,0,291,294,1,0,0,0,292,290,1,0,0,0,292,293,1,0,0, + 0,293,297,1,0,0,0,294,292,1,0,0,0,295,296,5,38,0,0,296,298,3,26,13,0,297, + 295,1,0,0,0,297,298,1,0,0,0,298,300,1,0,0,0,299,286,1,0,0,0,299,287,1,0, + 0,0,299,300,1,0,0,0,300,301,1,0,0,0,301,302,5,54,0,0,302,23,1,0,0,0,303, + 304,3,72,36,0,304,25,1,0,0,0,305,306,5,68,0,0,306,311,3,28,14,0,307,308, + 5,38,0,0,308,310,3,28,14,0,309,307,1,0,0,0,310,313,1,0,0,0,311,309,1,0, + 0,0,311,312,1,0,0,0,312,314,1,0,0,0,313,311,1,0,0,0,314,315,5,69,0,0,315, + 27,1,0,0,0,316,317,3,106,53,0,317,318,5,37,0,0,318,319,3,68,34,0,319,29, + 1,0,0,0,320,321,3,64,32,0,321,31,1,0,0,0,322,323,5,12,0,0,323,324,3,34, + 17,0,324,33,1,0,0,0,325,330,3,36,18,0,326,327,5,38,0,0,327,329,3,36,18, + 0,328,326,1,0,0,0,329,332,1,0,0,0,330,328,1,0,0,0,330,331,1,0,0,0,331,35, + 1,0,0,0,332,330,1,0,0,0,333,334,3,58,29,0,334,335,5,35,0,0,335,337,1,0, + 0,0,336,333,1,0,0,0,336,337,1,0,0,0,337,338,1,0,0,0,338,339,3,10,5,0,339, + 37,1,0,0,0,340,341,5,6,0,0,341,346,3,40,20,0,342,343,5,38,0,0,343,345,3, + 40,20,0,344,342,1,0,0,0,345,348,1,0,0,0,346,344,1,0,0,0,346,347,1,0,0,0, + 347,350,1,0,0,0,348,346,1,0,0,0,349,351,3,46,23,0,350,349,1,0,0,0,350,351, + 1,0,0,0,351,39,1,0,0,0,352,353,3,42,21,0,353,354,5,37,0,0,354,356,1,0,0, + 0,355,352,1,0,0,0,355,356,1,0,0,0,356,357,1,0,0,0,357,358,3,44,22,0,358, + 41,1,0,0,0,359,360,5,82,0,0,360,43,1,0,0,0,361,362,7,2,0,0,362,45,1,0,0, + 0,363,364,5,81,0,0,364,369,5,82,0,0,365,366,5,38,0,0,366,368,5,82,0,0,367, + 365,1,0,0,0,368,371,1,0,0,0,369,367,1,0,0,0,369,370,1,0,0,0,370,47,1,0, + 0,0,371,369,1,0,0,0,372,373,5,20,0,0,373,378,3,40,20,0,374,375,5,38,0,0, + 375,377,3,40,20,0,376,374,1,0,0,0,377,380,1,0,0,0,378,376,1,0,0,0,378,379, + 1,0,0,0,379,382,1,0,0,0,380,378,1,0,0,0,381,383,3,54,27,0,382,381,1,0,0, + 0,382,383,1,0,0,0,383,386,1,0,0,0,384,385,5,32,0,0,385,387,3,34,17,0,386, + 384,1,0,0,0,386,387,1,0,0,0,387,49,1,0,0,0,388,389,5,4,0,0,389,390,3,34, + 17,0,390,51,1,0,0,0,391,393,5,15,0,0,392,394,3,54,27,0,393,392,1,0,0,0, + 393,394,1,0,0,0,394,397,1,0,0,0,395,396,5,32,0,0,396,398,3,34,17,0,397, + 395,1,0,0,0,397,398,1,0,0,0,398,53,1,0,0,0,399,404,3,56,28,0,400,401,5, + 38,0,0,401,403,3,56,28,0,402,400,1,0,0,0,403,406,1,0,0,0,404,402,1,0,0, + 0,404,405,1,0,0,0,405,55,1,0,0,0,406,404,1,0,0,0,407,410,3,36,18,0,408, + 409,5,16,0,0,409,411,3,10,5,0,410,408,1,0,0,0,410,411,1,0,0,0,411,57,1, + 0,0,0,412,417,3,72,36,0,413,414,5,40,0,0,414,416,3,72,36,0,415,413,1,0, + 0,0,416,419,1,0,0,0,417,415,1,0,0,0,417,418,1,0,0,0,418,59,1,0,0,0,419, + 417,1,0,0,0,420,425,3,66,33,0,421,422,5,40,0,0,422,424,3,66,33,0,423,421, + 1,0,0,0,424,427,1,0,0,0,425,423,1,0,0,0,425,426,1,0,0,0,426,61,1,0,0,0, + 427,425,1,0,0,0,428,433,3,60,30,0,429,430,5,38,0,0,430,432,3,60,30,0,431, + 429,1,0,0,0,432,435,1,0,0,0,433,431,1,0,0,0,433,434,1,0,0,0,434,63,1,0, + 0,0,435,433,1,0,0,0,436,437,7,3,0,0,437,65,1,0,0,0,438,442,5,86,0,0,439, + 440,4,33,9,0,440,442,3,70,35,0,441,438,1,0,0,0,441,439,1,0,0,0,442,67,1, + 0,0,0,443,486,5,49,0,0,444,445,3,104,52,0,445,446,5,73,0,0,446,486,1,0, + 0,0,447,486,3,102,51,0,448,486,3,104,52,0,449,486,3,98,49,0,450,486,3,70, + 35,0,451,486,3,106,53,0,452,453,5,71,0,0,453,458,3,100,50,0,454,455,5,38, + 0,0,455,457,3,100,50,0,456,454,1,0,0,0,457,460,1,0,0,0,458,456,1,0,0,0, + 458,459,1,0,0,0,459,461,1,0,0,0,460,458,1,0,0,0,461,462,5,72,0,0,462,486, + 1,0,0,0,463,464,5,71,0,0,464,469,3,98,49,0,465,466,5,38,0,0,466,468,3,98, + 49,0,467,465,1,0,0,0,468,471,1,0,0,0,469,467,1,0,0,0,469,470,1,0,0,0,470, + 472,1,0,0,0,471,469,1,0,0,0,472,473,5,72,0,0,473,486,1,0,0,0,474,475,5, + 71,0,0,475,480,3,106,53,0,476,477,5,38,0,0,477,479,3,106,53,0,478,476,1, + 0,0,0,479,482,1,0,0,0,480,478,1,0,0,0,480,481,1,0,0,0,481,483,1,0,0,0,482, + 480,1,0,0,0,483,484,5,72,0,0,484,486,1,0,0,0,485,443,1,0,0,0,485,444,1, + 0,0,0,485,447,1,0,0,0,485,448,1,0,0,0,485,449,1,0,0,0,485,450,1,0,0,0,485, + 451,1,0,0,0,485,452,1,0,0,0,485,463,1,0,0,0,485,474,1,0,0,0,486,69,1,0, + 0,0,487,490,5,52,0,0,488,490,5,70,0,0,489,487,1,0,0,0,489,488,1,0,0,0,490, + 71,1,0,0,0,491,495,3,64,32,0,492,493,4,36,10,0,493,495,3,70,35,0,494,491, + 1,0,0,0,494,492,1,0,0,0,495,73,1,0,0,0,496,497,5,9,0,0,497,498,5,30,0,0, + 498,75,1,0,0,0,499,500,5,14,0,0,500,505,3,78,39,0,501,502,5,38,0,0,502, + 504,3,78,39,0,503,501,1,0,0,0,504,507,1,0,0,0,505,503,1,0,0,0,505,506,1, + 0,0,0,506,77,1,0,0,0,507,505,1,0,0,0,508,510,3,10,5,0,509,511,7,4,0,0,510, + 509,1,0,0,0,510,511,1,0,0,0,511,514,1,0,0,0,512,513,5,50,0,0,513,515,7, + 5,0,0,514,512,1,0,0,0,514,515,1,0,0,0,515,79,1,0,0,0,516,517,5,8,0,0,517, + 518,3,62,31,0,518,81,1,0,0,0,519,520,5,2,0,0,520,521,3,62,31,0,521,83,1, + 0,0,0,522,523,5,11,0,0,523,528,3,86,43,0,524,525,5,38,0,0,525,527,3,86, + 43,0,526,524,1,0,0,0,527,530,1,0,0,0,528,526,1,0,0,0,528,529,1,0,0,0,529, + 85,1,0,0,0,530,528,1,0,0,0,531,532,3,60,30,0,532,533,5,90,0,0,533,534,3, + 60,30,0,534,87,1,0,0,0,535,536,5,1,0,0,536,537,3,20,10,0,537,539,3,106, + 53,0,538,540,3,94,47,0,539,538,1,0,0,0,539,540,1,0,0,0,540,89,1,0,0,0,541, + 542,5,7,0,0,542,543,3,20,10,0,543,544,3,106,53,0,544,91,1,0,0,0,545,546, + 5,10,0,0,546,547,3,58,29,0,547,93,1,0,0,0,548,553,3,96,48,0,549,550,5,38, + 0,0,550,552,3,96,48,0,551,549,1,0,0,0,552,555,1,0,0,0,553,551,1,0,0,0,553, + 554,1,0,0,0,554,95,1,0,0,0,555,553,1,0,0,0,556,557,3,64,32,0,557,558,5, + 35,0,0,558,559,3,68,34,0,559,97,1,0,0,0,560,561,7,6,0,0,561,99,1,0,0,0, + 562,565,3,102,51,0,563,565,3,104,52,0,564,562,1,0,0,0,564,563,1,0,0,0,565, + 101,1,0,0,0,566,568,7,0,0,0,567,566,1,0,0,0,567,568,1,0,0,0,568,569,1,0, + 0,0,569,570,5,31,0,0,570,103,1,0,0,0,571,573,7,0,0,0,572,571,1,0,0,0,572, + 573,1,0,0,0,573,574,1,0,0,0,574,575,5,30,0,0,575,105,1,0,0,0,576,577,5, + 29,0,0,577,107,1,0,0,0,578,579,7,7,0,0,579,109,1,0,0,0,580,581,5,5,0,0, + 581,582,3,112,56,0,582,111,1,0,0,0,583,584,5,71,0,0,584,585,3,2,1,0,585, + 586,5,72,0,0,586,113,1,0,0,0,587,588,5,13,0,0,588,589,5,106,0,0,589,115, + 1,0,0,0,590,591,5,3,0,0,591,594,5,96,0,0,592,593,5,94,0,0,593,595,3,60, + 30,0,594,592,1,0,0,0,594,595,1,0,0,0,595,605,1,0,0,0,596,597,5,95,0,0,597, + 602,3,118,59,0,598,599,5,38,0,0,599,601,3,118,59,0,600,598,1,0,0,0,601, + 604,1,0,0,0,602,600,1,0,0,0,602,603,1,0,0,0,603,606,1,0,0,0,604,602,1,0, + 0,0,605,596,1,0,0,0,605,606,1,0,0,0,606,117,1,0,0,0,607,608,3,60,30,0,608, + 609,5,35,0,0,609,611,1,0,0,0,610,607,1,0,0,0,610,611,1,0,0,0,611,612,1, + 0,0,0,612,613,3,60,30,0,613,119,1,0,0,0,614,615,5,19,0,0,615,616,3,40,20, + 0,616,617,5,94,0,0,617,618,3,62,31,0,618,121,1,0,0,0,619,620,5,18,0,0,620, + 623,3,54,27,0,621,622,5,32,0,0,622,624,3,34,17,0,623,621,1,0,0,0,623,624, + 1,0,0,0,624,123,1,0,0,0,625,626,7,8,0,0,626,627,5,120,0,0,627,628,3,126, + 63,0,628,629,3,128,64,0,629,125,1,0,0,0,630,631,3,40,20,0,631,127,1,0,0, + 0,632,633,5,94,0,0,633,638,3,130,65,0,634,635,5,38,0,0,635,637,3,130,65, + 0,636,634,1,0,0,0,637,640,1,0,0,0,638,636,1,0,0,0,638,639,1,0,0,0,639,129, + 1,0,0,0,640,638,1,0,0,0,641,642,3,16,8,0,642,131,1,0,0,0,61,143,152,171, + 183,192,200,205,213,215,220,227,232,237,247,253,261,263,274,281,292,297, + 299,311,330,336,346,350,355,369,378,382,386,393,397,404,410,417,425,433, + 441,458,469,480,485,489,494,505,510,514,528,539,553,564,567,572,594,602, + 605,610,623,638]; private static __ATN: ATN; public static get _ATN(): ATN { @@ -4092,15 +4048,15 @@ export class ProcessingCommandContext extends ParserRuleContext { public mvExpandCommand(): MvExpandCommandContext { return this.getTypedRuleContext(MvExpandCommandContext, 0) as MvExpandCommandContext; } + public joinCommand(): JoinCommandContext { + return this.getTypedRuleContext(JoinCommandContext, 0) as JoinCommandContext; + } public inlinestatsCommand(): InlinestatsCommandContext { return this.getTypedRuleContext(InlinestatsCommandContext, 0) as InlinestatsCommandContext; } public lookupCommand(): LookupCommandContext { return this.getTypedRuleContext(LookupCommandContext, 0) as LookupCommandContext; } - public joinCommand(): JoinCommandContext { - return this.getTypedRuleContext(JoinCommandContext, 0) as JoinCommandContext; - } public get ruleIndex(): number { return esql_parser.RULE_processingCommand; } @@ -6578,8 +6534,8 @@ export class JoinCommandContext extends ParserRuleContext { super(parent, invokingState); this.parser = parser; } - public DEV_JOIN(): TerminalNode { - return this.getToken(esql_parser.DEV_JOIN, 0); + public JOIN(): TerminalNode { + return this.getToken(esql_parser.JOIN, 0); } public joinTarget(): JoinTargetContext { return this.getTypedRuleContext(JoinTargetContext, 0) as JoinTargetContext; @@ -6587,8 +6543,8 @@ export class JoinCommandContext extends ParserRuleContext { public joinCondition(): JoinConditionContext { return this.getTypedRuleContext(JoinConditionContext, 0) as JoinConditionContext; } - public DEV_JOIN_LOOKUP(): TerminalNode { - return this.getToken(esql_parser.DEV_JOIN_LOOKUP, 0); + public JOIN_LOOKUP(): TerminalNode { + return this.getToken(esql_parser.JOIN_LOOKUP, 0); } public DEV_JOIN_LEFT(): TerminalNode { return this.getToken(esql_parser.DEV_JOIN_LEFT, 0); @@ -6614,7 +6570,6 @@ export class JoinCommandContext extends ParserRuleContext { export class JoinTargetContext extends ParserRuleContext { public _index!: IndexPatternContext; - public _alias!: IdentifierContext; constructor(parser?: esql_parser, parent?: ParserRuleContext, invokingState?: number) { super(parent, invokingState); this.parser = parser; @@ -6622,12 +6577,6 @@ export class JoinTargetContext extends ParserRuleContext { public indexPattern(): IndexPatternContext { return this.getTypedRuleContext(IndexPatternContext, 0) as IndexPatternContext; } - public AS(): TerminalNode { - return this.getToken(esql_parser.AS, 0); - } - public identifier(): IdentifierContext { - return this.getTypedRuleContext(IdentifierContext, 0) as IdentifierContext; - } public get ruleIndex(): number { return esql_parser.RULE_joinTarget; } diff --git a/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.test.ts index 822508f05cae4..986c100248b18 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.test.ts @@ -14,7 +14,7 @@ describe('commands.where', () => { describe('.list()', () => { it('lists all "JOIN" commands', () => { const src = - 'FROM index | LIMIT 1 | JOIN join_index1 ON join_field1 | WHERE b == 2 | JOIN join_index2 ON join_field2 | LIMIT 1'; + 'FROM index | LIMIT 1 | LOOKUP JOIN join_index1 ON join_field1 | WHERE b == 2 | LOOKUP JOIN join_index2 ON join_field2 | LIMIT 1'; const query = EsqlQuery.fromSrc(src); const nodes = [...commands.join.list(query.ast)]; @@ -49,7 +49,7 @@ describe('commands.where', () => { describe('.byIndex()', () => { it('retrieves the specific "WHERE" command by index', () => { const src = - 'FROM index | LIMIT 1 | JOIN join_index1 ON join_field1 | WHERE b == 2 | JOIN join_index2 ON join_field2 | LIMIT 1'; + 'FROM index | LIMIT 1 | LOOKUP JOIN join_index1 ON join_field1 | WHERE b == 2 | LOOKUP JOIN join_index2 ON join_field2 | LIMIT 1'; const query = EsqlQuery.fromSrc(src); const node1 = commands.join.byIndex(query.ast, 1); @@ -83,7 +83,7 @@ describe('commands.where', () => { describe('.summarize', () => { it('returns target index fields', () => { const src = - 'FROM index | LIMIT 1 | JOIN join_index1 ON join_field1 | WHERE b == 2 | JOIN join_index2 ON join_field2 | LIMIT 1'; + 'FROM index | LIMIT 1 | LOOKUP JOIN join_index1 ON join_field1 | WHERE b == 2 | LOOKUP JOIN join_index2 ON join_field2 | LIMIT 1'; const query = EsqlQuery.fromSrc(src); const summary = commands.join.summarize(query.ast); @@ -107,35 +107,9 @@ describe('commands.where', () => { ]); }); - it('returns target aliases', () => { - const src = - 'FROM index | LIMIT 1 | JOIN join_index1 AS a ON join_field1 | WHERE b == 2 | JOIN join_index2 AS b ON join_field2 | LIMIT 1'; - const query = EsqlQuery.fromSrc(src); - const summary = commands.join.summarize(query.ast); - - expect(summary).toMatchObject([ - { - target: { - alias: { - type: 'identifier', - name: 'a', - }, - }, - }, - { - target: { - alias: { - type: 'identifier', - name: 'b', - }, - }, - }, - ]); - }); - it('captures join conditions', () => { const src = - 'FROM index | LIMIT 1 | JOIN join_index1 AS a ON join_field1 | WHERE b == 2 | JOIN join_index2 AS b ON join_field2, join_field3 | LIMIT 1'; + 'FROM index | LIMIT 1 | LOOKUP JOIN join_index1 ON join_field1 | WHERE b == 2 | LOOKUP JOIN join_index2 ON join_field2, join_field3 | LIMIT 1'; const query = EsqlQuery.fromSrc(src); const summary = commands.join.summarize(query.ast); @@ -162,5 +136,22 @@ describe('commands.where', () => { }, ]); }); + + it('extracts index of an incomplete query', () => { + const src = 'FROM kibana_sample_data_ecommerce | LOOKUP JOIN lookup_index ON '; + const query = EsqlQuery.fromSrc(src); + const summary = commands.join.summarize(query.ast); + + expect(summary).toMatchObject([ + { + target: { + index: { + type: 'source', + name: 'lookup_index', + }, + }, + }, + ]); + }); }); }); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.ts b/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.ts index 799cec22ae628..7216bce95ba63 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/mutate/commands/join/index.ts @@ -54,6 +54,41 @@ const getIdentifier = (node: WalkerAstNode): ESQLIdentifier => type: 'identifier', }) as ESQLIdentifier; +/** + * Summarizes a single JOIN command. + * + * @param command JOIN command to summarize. + * @returns Returns a summary of the JOIN command. + */ +export const summarizeCommand = (command: ESQLAstJoinCommand): JoinCommandSummary => { + const firstArg = command.args[0]; + let index: ESQLSource | undefined; + let alias: ESQLIdentifier | undefined; + const conditions: ESQLAstExpression[] = []; + + if (isAsExpression(firstArg)) { + index = getSource(firstArg.args[0]); + alias = getIdentifier(firstArg.args[1]); + } else { + index = getSource(firstArg); + } + + const on = generic.commands.options.find(command, ({ name }) => name === 'on'); + + conditions.push(...((on?.args || []) as ESQLAstExpression[])); + + const target: JoinCommandTarget = { + index: index!, + alias, + }; + const summary: JoinCommandSummary = { + target, + conditions, + }; + + return summary; +}; + /** * Summarizes all JOIN commands in the query. * @@ -65,30 +100,7 @@ export const summarize = (query: ESQLAstQueryExpression): JoinCommandSummary[] = const summaries: JoinCommandSummary[] = []; for (const command of list(query)) { - const firstArg = command.args[0]; - let index: ESQLSource | undefined; - let alias: ESQLIdentifier | undefined; - const conditions: ESQLAstExpression[] = []; - - if (isAsExpression(firstArg)) { - index = getSource(firstArg.args[0]); - alias = getIdentifier(firstArg.args[1]); - } else { - index = getSource(firstArg); - } - - const on = generic.commands.options.find(command, ({ name }) => name === 'on'); - - conditions.push(...((on?.args || []) as ESQLAstExpression[])); - - const target: JoinCommandTarget = { - index: index!, - alias, - }; - const summary: JoinCommandSummary = { - target, - conditions, - }; + const summary = summarizeCommand(command); summaries.push(summary); } diff --git a/src/platform/packages/shared/kbn-esql-ast/src/parser/__tests__/join.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/parser/__tests__/join.test.ts index e574ce4cdbd67..6bb6979415060 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/parser/__tests__/join.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/parser/__tests__/join.test.ts @@ -57,35 +57,8 @@ describe(' JOIN command', () => { }); }); - it('can parse out target with "AS" alias expression', () => { - const text = `FROM employees | LOOKUP JOIN languages_lookup AS ll ON language_code`; - const query = EsqlQuery.fromSrc(text); - - expect(query.ast.commands[1]).toMatchObject({ - commandType: 'lookup', - args: [ - { - type: 'function', - subtype: 'binary-expression', - name: 'as', - args: [ - { - type: 'source', - name: 'languages_lookup', - }, - { - type: 'identifier', - name: 'll', - }, - ], - }, - {}, - ], - }); - }); - it('can parse out a single "ON" predicate expression', () => { - const text = `FROM employees | LOOKUP JOIN languages_lookup AS ll ON language_code`; + const text = `FROM employees | LOOKUP JOIN languages_lookup ON language_code`; const query = EsqlQuery.fromSrc(text); expect(query.ast.commands[1]).toMatchObject({ @@ -113,7 +86,7 @@ describe(' JOIN command', () => { }); it('can parse out multiple "ON" predicate expressions', () => { - const text = `FROM employees | LOOKUP JOIN languages_lookup AS ll ON a, b, c`; + const text = `FROM employees | LOOKUP JOIN languages_lookup ON a, b, c`; const query = EsqlQuery.fromSrc(text); expect(query.ast.commands[1]).toMatchObject({ @@ -178,47 +151,33 @@ describe(' JOIN command', () => { }); it('correctly extracts node positions', () => { - const text = `FROM employees | LOOKUP JOIN index AS alias ON on_1, on_2 | LIMIT 1`; + const text = `FROM employees | LOOKUP JOIN index ON on_1, on_2 | LIMIT 1`; const query = EsqlQuery.fromSrc(text); const node1 = Walker.match(query.ast, { type: 'source', name: 'index' }); - const node2 = Walker.match(query.ast, { type: 'identifier', name: 'alias' }); - const node3 = Walker.match(query.ast, { type: 'column', name: 'on_1' }); - const node4 = Walker.match(query.ast, { type: 'column', name: 'on_2' }); - const node5 = Walker.match(query.ast, { type: 'function', name: 'as' }); + const node2 = Walker.match(query.ast, { type: 'column', name: 'on_1' }); + const node3 = Walker.match(query.ast, { type: 'column', name: 'on_2' }); expect(query.src.slice(node1?.location.min, node1?.location.max! + 1)).toBe('index'); - expect(query.src.slice(node2?.location.min, node2?.location.max! + 1)).toBe('alias'); - expect(query.src.slice(node3?.location.min, node3?.location.max! + 1)).toBe('on_1'); - expect(query.src.slice(node4?.location.min, node4?.location.max! + 1)).toBe('on_2'); - expect(query.src.slice(node5?.location.min, node5?.location.max! + 1)).toBe('index AS alias'); + expect(query.src.slice(node2?.location.min, node2?.location.max! + 1)).toBe('on_1'); + expect(query.src.slice(node3?.location.min, node3?.location.max! + 1)).toBe('on_2'); }); it('correctly extracts JOIN command position', () => { - const text = `FROM employees | LOOKUP JOIN index AS alias ON on_1, on_2 | LIMIT 1`; + const text = `FROM employees | LOOKUP JOIN index ON on_1, on_2 | LIMIT 1`; const query = EsqlQuery.fromSrc(text); const join = Walker.match(query.ast, { type: 'command', name: 'join' }); expect(query.src.slice(join?.location.min, join?.location.max! + 1)).toBe( - 'LOOKUP JOIN index AS alias ON on_1, on_2' + 'LOOKUP JOIN index ON on_1, on_2' ); }); it('correctly extracts ON option position', () => { - const text = `FROM employees | LOOKUP JOIN index AS alias ON on_1, on_2 | LIMIT 1`; + const text = `FROM employees | LOOKUP JOIN index ON on_1, on_2 | LIMIT 1`; const query = EsqlQuery.fromSrc(text); const on = Walker.match(query.ast, { type: 'option', name: 'on' }); expect(query.src.slice(on?.location.min, on?.location.max! + 1)).toBe('ON on_1, on_2'); }); }); - - describe('incorrectly formatted', () => { - it('throws error on invalid "AS" keyword', () => { - const text = `FROM employees | LOOKUP JOIN index AAS alias ON on_1, on_2 | LIMIT 1`; - const query = EsqlQuery.fromSrc(text); - - expect(query.errors.length > 0).toBe(true); - expect(query.errors[0].message.includes('AAS')).toBe(true); - }); - }); }); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/parser/factories/join.ts b/src/platform/packages/shared/kbn-esql-ast/src/parser/factories/join.ts index 14d1f3797385c..2e254cf954bd6 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/parser/factories/join.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/parser/factories/join.ts @@ -8,39 +8,12 @@ */ import { JoinCommandContext, JoinTargetContext } from '../../antlr/esql_parser'; -import { - ESQLAstItem, - ESQLBinaryExpression, - ESQLCommand, - ESQLIdentifier, - ESQLSource, -} from '../../types'; -import { - createBinaryExpression, - createCommand, - createIdentifier, - createOption, - createSource, -} from '../factories'; +import { ESQLAstItem, ESQLCommand, ESQLIdentifier, ESQLSource } from '../../types'; +import { createCommand, createOption, createSource } from '../factories'; import { visitValueExpression } from '../walkers'; -const createNodeFromJoinTarget = ( - ctx: JoinTargetContext -): ESQLSource | ESQLIdentifier | ESQLBinaryExpression => { - const index = createSource(ctx._index); - const aliasCtx = ctx._alias; - - if (!aliasCtx) { - return index; - } - - const alias = createIdentifier(aliasCtx); - const renameExpression = createBinaryExpression('as', ctx, [ - index, - alias, - ]) as ESQLBinaryExpression; - - return renameExpression; +const createNodeFromJoinTarget = (ctx: JoinTargetContext): ESQLSource | ESQLIdentifier => { + return createSource(ctx._index); }; export const createJoinCommand = (ctx: JoinCommandContext): ESQLCommand => { diff --git a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.comments.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.comments.test.ts index d725759172928..bc34fa7bac9cc 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.comments.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.comments.test.ts @@ -188,11 +188,11 @@ describe('rename expressions', () => { describe('commands', () => { describe('JOIN', () => { test('around JOIN targets', () => { - assertPrint('FROM a | LEFT JOIN /*1*/ a /*2*/ AS /*3*/ b /*4*/ ON c'); + assertPrint('FROM a | LEFT JOIN /*1*/ a /*2*/ /*3*/ /*4*/'); }); test('around JOIN conditions', () => { - assertPrint('FROM a | LEFT JOIN a AS b ON /*1*/ c /*2*/, /*3*/ d /*4*/'); + assertPrint('FROM a | LEFT JOIN a /*1*/ /*2*/ /*3*/ /*4*/'); }); }); }); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts index 004e022e41e69..fd6dac9c532f2 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts @@ -133,15 +133,6 @@ describe('single line query', () => { ); }); - test('supports aliases', () => { - const { text } = reprint(` - FROM employees | LEFT JOIN languages_lookup AS something ON language_code`); - - expect(text).toBe( - 'FROM employees | LEFT JOIN languages_lookup AS something ON language_code' - ); - }); - test('supports multiple conditions', () => { const { text } = reprint(` FROM employees | LEFT JOIN a ON b, c, d.e.f`); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.comments.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.comments.test.ts index 32db036cf0ebd..b9f418badfb4a 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.comments.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.comments.test.ts @@ -505,7 +505,7 @@ ROW 1 // 2 /* 3 */ // 4 - /* 5 */ a /* 6 */ AS /* 7 */ b + /* 5 */ a /* 6 */ /* 7 */ ON c`; const text = reprint(query).text; expect('\n' + text).toBe(` @@ -515,14 +515,13 @@ FROM index // 2 /* 3 */ // 4 - /* 5 */ a /* 6 */ AS - /* 7 */ b + /* 5 */ a /* 6 */ /* 7 */ ON c`); }); test('JOIN "ON" option argument comments', () => { const query = ` - FROM index | RIGHT JOIN a AS b ON + FROM index | RIGHT JOIN a ON // c.1 /* c.2 */ c /* c.3 */, // d.1 @@ -531,7 +530,7 @@ FROM index expect('\n' + text).toBe(` FROM index | RIGHT JOIN - a AS b + a ON // c.1 /* c.2 */ c, /* c.3 */ diff --git a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts index bad4884e94a24..bd3c6b17a7bbd 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/__tests__/wrapping_pretty_printer.test.ts @@ -22,19 +22,19 @@ const reprint = (src: string, opts?: WrappingPrettyPrinterOptions) => { describe('commands', () => { describe('JOIN', () => { test('with short identifiers', () => { - const { text } = reprint('FROM a | RIGHT JOIN b AS c ON d, e'); + const { text } = reprint('FROM a | RIGHT JOIN b ON d, e'); - expect(text).toBe('FROM a | RIGHT JOIN b AS c ON d, e'); + expect(text).toBe('FROM a | RIGHT JOIN b ON d, e'); }); test('with long identifiers', () => { const { text } = reprint( - 'FROM aaaaaaaaaaaa | RIGHT JOIN bbbbbbbbbbbbbbbbb AS cccccccccccccccccccc ON dddddddddddddddddddddddddddddddddddddddd, eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' + 'FROM aaaaaaaaaaaa | RIGHT JOIN bbbbbbbbbbbbbbbbb ON dddddddddddddddddddddddddddddddddddddddd, eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' ); expect('\n' + text).toBe(` FROM aaaaaaaaaaaa - | RIGHT JOIN bbbbbbbbbbbbbbbbb AS cccccccccccccccccccc + | RIGHT JOIN bbbbbbbbbbbbbbbbb ON dddddddddddddddddddddddddddddddddddddddd, eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/leaf_printer.ts b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/leaf_printer.ts index 76e6536070b71..0229fa975ad9a 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/leaf_printer.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/pretty_print/leaf_printer.ts @@ -169,8 +169,11 @@ export const LeafPrinter = { return text; }, - print: (node: ESQLProperNode): string => { + print: (node: ESQLProperNode | ESQLAstComment): string => { switch (node.type) { + case 'source': { + return LeafPrinter.source(node); + } case 'identifier': { return LeafPrinter.identifier(node); } @@ -183,6 +186,9 @@ export const LeafPrinter = { case 'timeInterval': { return LeafPrinter.timeInterval(node); } + case 'comment': { + return LeafPrinter.comment(node); + } } return ''; }, diff --git a/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/commands.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/commands.test.ts index 35bfda59070c8..2c028492a0c12 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/commands.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/commands.test.ts @@ -57,7 +57,7 @@ test('can visit JOIN command arguments', () => { const { ast } = EsqlQuery.fromSrc(` FROM index | STATS 1, "str", [true], a = b BY field - | RIGHT JOIN abc AS xxx ON xyz + | RIGHT JOIN abc ON xyz | LIMIT 123 `); const visitor = new Visitor() @@ -82,14 +82,14 @@ test('can visit JOIN command arguments', () => { }); const list = visitor.visitQuery(ast).flat().filter(Boolean); - expect(list).toMatchObject(['as']); + expect(list).toMatchObject([]); }); test('can visit JOIN ON option', () => { const { ast } = EsqlQuery.fromSrc(` FROM index | STATS 1, "str", [true], a = b BY field - | RIGHT JOIN abc AS xxx ON xyz + | RIGHT JOIN abc ON xyz | LIMIT 123 `); const visitor = new Visitor() diff --git a/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/expressions.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/expressions.test.ts index a50ae64568939..a32ef353d155e 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/expressions.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/visitor/__tests__/expressions.test.ts @@ -190,7 +190,7 @@ test('"visitExpression" does visit WHERE clause args', () => { test('"visitExpression" does visit identifier nodes', () => { const { ast } = parse(` FROM index - | RIGHT JOIN a AS b ON c + | RIGHT JOIN a ON c `); const expressions: string[] = []; new Visitor() @@ -206,5 +206,5 @@ test('"visitExpression" does visit identifier nodes', () => { }) .visitQuery(ast); - expect(expressions.sort()).toEqual(['a', 'as', 'b', 'index']); + expect(expressions.sort()).toEqual(['a', 'index']); }); diff --git a/src/platform/packages/shared/kbn-esql-ast/src/walker/walker.test.ts b/src/platform/packages/shared/kbn-esql-ast/src/walker/walker.test.ts index bc13f6f9dcf11..092a83c9cd182 100644 --- a/src/platform/packages/shared/kbn-esql-ast/src/walker/walker.test.ts +++ b/src/platform/packages/shared/kbn-esql-ast/src/walker/walker.test.ts @@ -84,7 +84,7 @@ describe('structurally can walk all nodes', () => { }); test('can traverse JOIN command', () => { - const { ast } = parse('FROM index | LEFT JOIN a AS b ON c, d'); + const { ast } = parse('FROM index | LEFT JOIN a ON c, d'); const commands: ESQLCommand[] = []; const sources: ESQLSource[] = []; const identifiers: ESQLIdentifier[] = []; @@ -99,7 +99,7 @@ describe('structurally can walk all nodes', () => { expect(commands.map(({ name }) => name).sort()).toStrictEqual(['from', 'join']); expect(sources.map(({ name }) => name).sort()).toStrictEqual(['a', 'index']); - expect(identifiers.map(({ name }) => name).sort()).toStrictEqual(['as', 'b', 'c', 'd']); + expect(identifiers.map(({ name }) => name).sort()).toStrictEqual(['c', 'd']); expect(columns.map(({ name }) => name).sort()).toStrictEqual(['c', 'd']); }); diff --git a/src/platform/packages/shared/kbn-esql-utils/index.ts b/src/platform/packages/shared/kbn-esql-utils/index.ts index ae7b0c527377e..33a6047a1c98c 100644 --- a/src/platform/packages/shared/kbn-esql-utils/index.ts +++ b/src/platform/packages/shared/kbn-esql-utils/index.ts @@ -39,6 +39,7 @@ export { queryCannotBeSampled, mapVariableToColumn, getValuesFromQueryField, + getESQLQueryVariables, } from './src'; export { ENABLE_ESQL, FEEDBACK_LINK } from './constants'; diff --git a/src/platform/packages/shared/kbn-esql-utils/src/index.ts b/src/platform/packages/shared/kbn-esql-utils/src/index.ts index e07b11564b882..0df4dabf455ca 100644 --- a/src/platform/packages/shared/kbn-esql-utils/src/index.ts +++ b/src/platform/packages/shared/kbn-esql-utils/src/index.ts @@ -23,6 +23,7 @@ export { getQueryColumnsFromESQLQuery, mapVariableToColumn, getValuesFromQueryField, + getESQLQueryVariables, } from './utils/query_parsing_helpers'; export { queryCannotBeSampled } from './utils/query_cannot_be_sampled'; export { diff --git a/src/platform/packages/shared/kbn-esql-utils/src/utils/query_parsing_helpers.ts b/src/platform/packages/shared/kbn-esql-utils/src/utils/query_parsing_helpers.ts index 0d9269f23a12d..7d9f08b26dce5 100644 --- a/src/platform/packages/shared/kbn-esql-utils/src/utils/query_parsing_helpers.ts +++ b/src/platform/packages/shared/kbn-esql-utils/src/utils/query_parsing_helpers.ts @@ -149,6 +149,13 @@ export const getQueryColumnsFromESQLQuery = (esql: string): string[] => { return columns.map((column) => column.name); }; + +export const getESQLQueryVariables = (esql: string): string[] => { + const { root } = parse(esql); + const usedVariablesInQuery = Walker.params(root); + return usedVariablesInQuery.map((v) => v.text.replace('?', '')); +}; + /** * This function is used to map the variables to the columns in the datatable * @param esql:string @@ -164,12 +171,8 @@ export const mapVariableToColumn = ( if (!variables.length) { return columns; } - const { root } = parse(esql); - const usedVariablesInQuery = Walker.params(root); - - const uniqueVariablesInQyery = new Set( - usedVariablesInQuery.map((v) => v.text.replace('?', '')) - ); + const usedVariablesInQuery = getESQLQueryVariables(esql); + const uniqueVariablesInQyery = new Set(usedVariablesInQuery); columns.map((column) => { if (variables.some((variable) => variable.value === column.id)) { diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.join.test.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.join.test.ts index 16b0745435184..cfb1cdaa2f83c 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.join.test.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.join.test.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { setup, getFieldNamesByType } from './helpers'; +import { setup, getFieldNamesByType, lookupIndexFields } from './helpers'; describe('autocomplete.suggest', () => { describe(' JOIN [ AS ] ON [, [, ...]]', () => { @@ -103,24 +103,34 @@ describe('autocomplete.suggest', () => { test('suggests fields after ON keyword', async () => { const { suggest } = await setup(); - const suggestions = await suggest('FROM index | LOOKUP JOIN join_index ON /'); - const labels = suggestions.map((s) => s.text).sort(); + const labels = suggestions.map((s) => s.text.trim()).sort(); const expected = getFieldNamesByType('any') .sort() - .map((field) => field + ' '); + .map((field) => field.trim()); + + for (const { name } of lookupIndexFields) { + expected.push(name.trim()); + } + + expected.sort(); expect(labels).toEqual(expected); }); test('more field suggestions after comma', async () => { const { suggest } = await setup(); - const suggestions = await suggest('FROM index | LOOKUP JOIN join_index ON stringField, /'); - const labels = suggestions.map((s) => s.text).sort(); + const labels = suggestions.map((s) => s.text.trim()).sort(); const expected = getFieldNamesByType('any') .sort() - .map((field) => field + ' '); + .map((field) => field.trim()); + + for (const { name } of lookupIndexFields) { + expected.push(name.trim()); + } + + expected.sort(); expect(labels).toEqual(expected); }); diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts index 0d62b5040c034..27eeeecad99f9 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts @@ -48,7 +48,9 @@ export const TIME_PICKER_SUGGESTION: PartialSuggestionWithText = { export const triggerCharacters = [',', '(', '=', ' ']; -export const fields: Array = [ +export type TestField = ESQLRealField & { suggestedAs?: string }; + +export const fields: TestField[] = [ ...fieldTypes.map((type) => ({ name: `${camelCase(type)}Field`, type, @@ -57,6 +59,12 @@ export const fields: Array = [ { name: 'kubernetes.something.something', type: 'double' }, ]; +export const lookupIndexFields: TestField[] = [ + { name: 'booleanField', type: 'boolean' }, + { name: 'dateField', type: 'date' }, + { name: 'joinIndexOnlyField', type: 'text' }, +]; + export const indexes = ( [] as Array<{ name: string; hidden: boolean; suggestedAs?: string }> ).concat( @@ -279,7 +287,13 @@ export function createCustomCallbackMocks( const finalSources = customSources || indexes; const finalPolicies = customPolicies || policies; return { - getColumnsFor: jest.fn(async () => finalColumnsSinceLastCommand), + getColumnsFor: jest.fn(async ({ query }) => { + if (query === 'FROM join_index') { + return lookupIndexFields; + } + + return finalColumnsSinceLastCommand; + }), getSources: jest.fn(async () => finalSources), getPolicies: jest.fn(async () => finalPolicies), getJoinIndices: jest.fn(async () => ({ indices: joinIndices })), diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/index.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/index.ts index 70314bea364f3..302061fac0736 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/index.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/index.ts @@ -8,8 +8,8 @@ */ import { i18n } from '@kbn/i18n'; -import { type ESQLAstItem, ESQLAst } from '@kbn/esql-ast'; -import { ESQLCommand } from '@kbn/esql-ast/src/types'; +import { type ESQLAstItem, ESQLAst, ESQLCommand, mutate, LeafPrinter } from '@kbn/esql-ast'; +import type { ESQLAstJoinCommand } from '@kbn/esql-ast'; import type { ESQLCallbacks } from '../../../shared/types'; import { CommandBaseDefinition, @@ -17,8 +17,13 @@ import { CommandTypeDefinition, type SupportedDataType, } from '../../../definitions/types'; -import { getPosition, joinIndicesToSuggestions } from './util'; -import { TRIGGER_SUGGESTION_COMMAND } from '../../factories'; +import { + getPosition, + joinIndicesToSuggestions, + suggestionIntersection, + suggestionUnion, +} from './util'; +import { TRIGGER_SUGGESTION_COMMAND, buildFieldsDefinitionsWithMetadata } from '../../factories'; import type { GetColumnsByTypeFn, SuggestionRawDefinition } from '../../types'; import { commaCompleteItem, pipeCompleteItem } from '../../complete_items'; @@ -37,6 +42,60 @@ const getFullCommandMnemonics = ( ]); }; +const suggestFields = async ( + command: ESQLCommand<'join'>, + getColumnsByType: GetColumnsByTypeFn, + callbacks?: ESQLCallbacks +) => { + const summary = mutate.commands.join.summarizeCommand(command as ESQLAstJoinCommand); + const joinIndexPattern = LeafPrinter.print(summary.target.index); + + const [lookupIndexFields, sourceFields] = await Promise.all([ + callbacks?.getColumnsFor?.({ query: `FROM ${joinIndexPattern}` }), + getColumnsByType(['any'], [], { + advanceCursor: true, + openSuggestions: true, + }), + ]); + + const supportsControls = callbacks?.canSuggestVariables?.() ?? false; + const getVariablesByType = callbacks?.getVariablesByType; + const joinFields = buildFieldsDefinitionsWithMetadata( + lookupIndexFields!, + { supportsControls }, + getVariablesByType + ); + + const intersection = suggestionIntersection(joinFields, sourceFields); + const union = suggestionUnion(sourceFields, joinFields); + + for (const commonField of intersection) { + commonField.sortText = '1'; + commonField.documentation = { + value: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.join.sharedField', { + defaultMessage: 'Field shared between the source and the lookup index', + }), + }; + + let detail = commonField.detail || ''; + + if (detail) { + detail += ' '; + } + + detail += i18n.translate( + 'kbn-esql-validation-autocomplete.esql.autocomplete.join.commonFieldNote', + { + defaultMessage: '(common field)', + } + ); + + commonField.detail = detail; + } + + return [...intersection, ...union]; +}; + export const suggest: CommandBaseDefinition<'join'>['suggest'] = async ( innerText: string, command: ESQLCommand<'join'>, @@ -113,10 +172,7 @@ export const suggest: CommandBaseDefinition<'join'>['suggest'] = async ( } case 'after_on': { - const fields = await getColumnsByType(['any'], [], { - advanceCursor: true, - openSuggestions: true, - }); + const fields = await suggestFields(command, getColumnsByType, callbacks); return fields; } @@ -127,10 +183,7 @@ export const suggest: CommandBaseDefinition<'join'>['suggest'] = async ( const commaIsLastToken = !!match?.groups?.comma; if (commaIsLastToken) { - const fields = await getColumnsByType(['any'], [], { - advanceCursor: true, - openSuggestions: true, - }); + const fields = await suggestFields(command, getColumnsByType, callbacks); return fields; } diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.test.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.test.ts index a19fffd8e1fb3..7135caa5e2b25 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.test.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.test.ts @@ -8,7 +8,13 @@ */ import { joinIndices } from '../../../__tests__/helpers'; -import { getPosition, joinIndicesToSuggestions } from './util'; +import { + getPosition, + joinIndicesToSuggestions, + suggestionIntersection, + suggestionUnion, +} from './util'; +import { SuggestionRawDefinition } from '../../types'; describe('getPosition()', () => { test('returns correct position on complete modifier matches', () => { @@ -68,3 +74,165 @@ describe('joinIndicesToSuggestions()', () => { ]); }); }); + +describe('suggestionIntersection()', () => { + test('returns shared fields between two lists', () => { + const intersection = suggestionIntersection( + [ + { label: 'id', text: '', kind: 'Field', detail: '' }, + { label: 'currency', text: '', kind: 'Field', detail: '' }, + { label: 'value', text: '', kind: 'Field', detail: '' }, + { label: 'timestamp', text: '', kind: 'Field', detail: '' }, + ], + [ + { label: 'id', text: '', kind: 'Field', detail: '' }, + { label: 'currency', text: '', kind: 'Field', detail: '' }, + { label: 'name', text: '', kind: 'Field', detail: '' }, + ] + ); + + expect(intersection).toEqual([ + { + label: 'id', + text: '', + kind: 'Field', + detail: '', + }, + { + label: 'currency', + text: '', + kind: 'Field', + detail: '', + }, + ]); + }); + + test('returns empty list if there are no shared fields', () => { + const intersection = suggestionIntersection( + [ + { label: 'id1', text: '', kind: 'Field', detail: '' }, + { label: 'value', text: '', kind: 'Field', detail: '' }, + { label: 'timestamp', text: '', kind: 'Field', detail: '' }, + ], + [ + { label: 'id2', text: '', kind: 'Field', detail: '' }, + { label: 'currency', text: '', kind: 'Field', detail: '' }, + { label: 'name', text: '', kind: 'Field', detail: '' }, + ] + ); + + expect(intersection).toEqual([]); + }); + + test('returns all fields, if all intersect', () => { + const intersection = suggestionIntersection( + [ + { label: 'id1', text: '', kind: 'Field', detail: '' }, + { label: 'value', text: '', kind: 'Field', detail: '' }, + { label: 'timestamp', text: '', kind: 'Field', detail: '' }, + ], + [ + { label: 'id1', text: '', kind: 'Field', detail: '' }, + { label: 'value', text: '', kind: 'Field', detail: '' }, + ] + ); + + expect(intersection).toEqual([ + { label: 'id1', text: '', kind: 'Field', detail: '' }, + { label: 'value', text: '', kind: 'Field', detail: '' }, + ]); + }); + + test('creates a clone of suggestions', () => { + const suggestion1: SuggestionRawDefinition = { + label: 'id1', + text: '', + kind: 'Field', + detail: '', + }; + const suggestion2: SuggestionRawDefinition = { + label: 'id1', + text: '', + kind: 'Field', + detail: '', + }; + const intersection = suggestionIntersection([suggestion1], [suggestion2]); + + expect(intersection).toEqual([suggestion1]); + expect(intersection[0]).not.toBe(suggestion1); + expect(intersection[0]).not.toBe(suggestion2); + }); +}); + +describe('suggestionUnion()', () => { + test('combines two sets without duplicates', () => { + const intersection = suggestionUnion( + [ + { label: 'id', text: '', kind: 'Field', detail: '' }, + { label: 'currency', text: '', kind: 'Field', detail: '' }, + { label: 'value', text: '', kind: 'Field', detail: '' }, + { label: 'timestamp', text: '', kind: 'Field', detail: '' }, + ], + [ + { label: 'id', text: '', kind: 'Field', detail: '' }, + { label: 'currency', text: '', kind: 'Field', detail: '' }, + { label: 'name', text: '', kind: 'Field', detail: '' }, + ] + ); + + expect(intersection).toEqual([ + { + label: 'id', + text: '', + kind: 'Field', + detail: '', + }, + { + label: 'currency', + text: '', + kind: 'Field', + detail: '', + }, + { + label: 'value', + text: '', + kind: 'Field', + detail: '', + }, + { + label: 'timestamp', + text: '', + kind: 'Field', + detail: '', + }, + { + label: 'name', + text: '', + kind: 'Field', + detail: '', + }, + ]); + }); + + test('combines two non-overlapping sets', () => { + const intersection = suggestionUnion( + [{ label: 'id', text: '', kind: 'Field', detail: '' }], + [{ label: 'currency', text: '', kind: 'Field', detail: '' }] + ); + + expect(intersection).toEqual([ + { + label: 'id', + text: '', + kind: 'Field', + detail: '', + }, + { + label: 'currency', + text: '', + kind: 'Field', + detail: '', + }, + ]); + }); +}); diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.ts index eae48cef75b4b..c5c3546dcf3e1 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/autocomplete/commands/join/util.ts @@ -10,8 +10,8 @@ import { ESQLCommand } from '@kbn/esql-ast'; import { i18n } from '@kbn/i18n'; import { JoinCommandPosition, JoinPosition, JoinStaticPosition } from './types'; -import type { JoinIndexAutocompleteItem } from '../../../validation/types'; import { SuggestionRawDefinition } from '../../types'; +import type { JoinIndexAutocompleteItem } from '../../../validation/types'; const REGEX = /^(?\w+((?\s+((?(JOIN|JOI|JO|J)((?\s+((?\S+((?\s+(?(AS|A))?(?\s+(((?\S+)?(?\s+)?)?))?((?(ON|O)((?\s+(?[^\s])?)?))?))?))?))?))?))?))?/i; @@ -105,3 +105,51 @@ export const joinIndicesToSuggestions = ( return [...mainSuggestions, ...aliasSuggestions]; }; + +export const suggestionIntersection = ( + suggestions1: SuggestionRawDefinition[], + suggestions2: SuggestionRawDefinition[] +): SuggestionRawDefinition[] => { + const labels1 = new Set(); + const intersection: SuggestionRawDefinition[] = []; + + for (const suggestion1 of suggestions1) { + labels1.add(suggestion1.label); + } + + for (const suggestion2 of suggestions2) { + if (labels1.has(suggestion2.label)) { + intersection.push({ ...suggestion2 }); + } + } + + return intersection; +}; + +export const suggestionUnion = ( + suggestions1: SuggestionRawDefinition[], + suggestions2: SuggestionRawDefinition[] +): SuggestionRawDefinition[] => { + const labels = new Set(); + const union: SuggestionRawDefinition[] = []; + + for (const suggestion of suggestions1) { + const label = suggestion.label; + + if (!labels.has(label)) { + union.push(suggestion); + labels.add(label); + } + } + + for (const suggestion of suggestions2) { + const label = suggestion.label; + + if (!labels.has(label)) { + union.push(suggestion); + labels.add(label); + } + } + + return union; +}; diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/operators.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/operators.ts index 0e7a276b88147..0fe8cb0576e7a 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/operators.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/operators.ts @@ -2823,7 +2823,7 @@ const likeDefinition: FunctionDefinition = { }, ], supportedCommands: ['eval', 'where', 'row', 'sort'], - supportedOptions: ['by'], + supportedOptions: undefined, validate: undefined, examples: ['FROM employees\n| WHERE first_name LIKE """?b*"""\n| KEEP first_name, last_name'], }; @@ -2834,7 +2834,7 @@ const matchOperatorDefinition: FunctionDefinition = { name: ':', description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.match_operator', { defaultMessage: - 'Use `MATCH` to perform a match query on the specified field.\nUsing `MATCH` is equivalent to using the `match` query in the Elasticsearch Query DSL.\n\nMatch can be used on fields from the text family like text and semantic_text,\nas well as other field types like keyword, boolean, dates, and numeric types.\n\nFor a simplified syntax, you can use the match operator `:` operator instead of `MATCH`.\n\n`MATCH` returns true if the provided query matches the row.', + 'Use the match operator (`:`) to perform a match query on the specified field.\nUsing `:` is equivalent to using the `match` query in the Elasticsearch Query DSL.\n\nThe match operator is equivalent to the match function.\n\nFor using the function syntax, or adding match query parameters, you can use the\nmatch function.\n\n`:` returns true if the provided query matches the row.', }), preview: true, alias: undefined, @@ -4680,7 +4680,7 @@ const rlikeDefinition: FunctionDefinition = { }, ], supportedCommands: ['eval', 'where', 'row', 'sort'], - supportedOptions: ['by'], + supportedOptions: undefined, validate: undefined, examples: [ 'FROM employees\n| WHERE first_name RLIKE """.leja.*"""\n| KEEP first_name, last_name', diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts index 7a8deced8f9de..2ca63fd8ce63e 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/generated/scalar_functions.ts @@ -1581,6 +1581,21 @@ const dateExtractDefinition: FunctionDefinition = { ], returnType: 'long', }, + { + params: [ + { + name: 'datePart', + type: 'keyword', + optional: false, + }, + { + name: 'date', + type: 'date_nanos', + optional: false, + }, + ], + returnType: 'long', + }, { params: [ { @@ -1596,6 +1611,21 @@ const dateExtractDefinition: FunctionDefinition = { ], returnType: 'long', }, + { + params: [ + { + name: 'datePart', + type: 'text', + optional: false, + }, + { + name: 'date', + type: 'date_nanos', + optional: false, + }, + ], + returnType: 'long', + }, ], supportedCommands: ['stats', 'inlinestats', 'metrics', 'eval', 'where', 'row', 'sort'], supportedOptions: ['by'], @@ -3696,7 +3726,7 @@ const matchDefinition: FunctionDefinition = { name: 'match', description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.match', { defaultMessage: - 'Use `MATCH` to perform a match query on the specified field.\nUsing `MATCH` is equivalent to using the `match` query in the Elasticsearch Query DSL.\n\nMatch can be used on fields from the text family like text and semantic_text,\nas well as other field types like keyword, boolean, dates, and numeric types.\n\nFor a simplified syntax, you can use the match operator `:` operator instead of `MATCH`.\n\n`MATCH` returns true if the provided query matches the row.', + 'Use `MATCH` to perform a match query on the specified field.\nUsing `MATCH` is equivalent to using the `match` query in the Elasticsearch Query DSL.\n\nMatch can be used on fields from the text family like text and semantic_text,\nas well as other field types like keyword, boolean, dates, and numeric types.\n\nMatch can use function named parameters to specify additional options for the match query.\nAll match query parameters are supported.\n\nFor a simplified syntax, you can use the match operator `:` operator instead of `MATCH`.\n\n`MATCH` returns true if the provided query matches the row.', }), preview: true, alias: undefined, @@ -3715,6 +3745,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3732,6 +3770,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3749,6 +3795,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3766,6 +3820,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3783,6 +3845,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3800,6 +3870,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3817,6 +3895,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3834,6 +3920,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3851,6 +3945,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3868,6 +3970,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3885,6 +3995,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3902,6 +4020,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3919,6 +4045,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3936,6 +4070,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3953,6 +4095,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3970,6 +4120,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -3987,6 +4145,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4004,6 +4170,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4021,6 +4195,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4038,6 +4220,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4055,6 +4245,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4072,6 +4270,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4089,6 +4295,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4106,6 +4320,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4123,6 +4345,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4140,6 +4370,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4157,6 +4395,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4174,6 +4420,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4191,6 +4445,14 @@ const matchDefinition: FunctionDefinition = { optional: false, constantOnly: true, }, + { + name: 'options', + type: 'function named parameters', + mapParams: + "{name='fuzziness', values=[AUTO, 1, 2], description='Maximum edit distance allowed for matching.'}, {name='auto_generate_synonyms_phrase_query', values=[true, false], description='If true, match phrase queries are automatically created for multi-term synonyms.'}, {name='analyzer', values=[standard], description='Analyzer used to convert the text in the query value into token.'}, {name='minimum_should_match', values=[2], description='Minimum number of clauses that must match for a document to be returned.'}, {name='zero_terms_query', values=[none, all], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='boost', values=[2.5], description='Floating point number used to decrease or increase the relevance scores of the query.'}, {name='fuzzy_transpositions', values=[true, false], description='If true, edits for fuzzy matching include transpositions of two adjacent characters (ab → ba).'}, {name='fuzzy_rewrite', values=[constant_score_blended, constant_score, constant_score_boolean, top_terms_blended_freqs_N, top_terms_boost_N, top_terms_N], description='Method used to rewrite the query. See the rewrite parameter for valid values and more information.'}, {name='prefix_length', values=[1], description='Number of beginning characters left unchanged for fuzzy matching.'}, {name='lenient', values=[true, false], description='If false, format-based errors, such as providing a text query value for a numeric field, are returned.'}, {name='operator', values=[AND, OR], description='Boolean logic used to interpret text in the query value.'}, {name='max_expansions', values=[50], description='Maximum number of terms to which the query will expand.'}", + optional: true, + constantOnly: true, + }, ], returnType: 'boolean', }, @@ -4200,6 +4462,7 @@ const matchDefinition: FunctionDefinition = { validate: undefined, examples: [ 'FROM books \n| WHERE MATCH(author, "Faulkner")\n| KEEP book_no, author \n| SORT book_no \n| LIMIT 5;', + 'FROM books \n| WHERE MATCH(title, "Hobbit Back Again", {"operator": "AND"})\n| KEEP title;', ], }; diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/types.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/types.ts index a894d3ab271ae..43a0664872450 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/types.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/definitions/types.ts @@ -42,6 +42,8 @@ export const fieldTypes = [ 'counter_double', 'unsupported', 'date_nanos', + // This type is inconsistent in comparison to the others. This is how it comes from ES though. + 'function named parameters', ] as const; export type FieldType = (typeof fieldTypes)[number]; @@ -162,6 +164,7 @@ export interface Signature { * values that we don't want to show as suggestions. */ literalSuggestions?: string[]; + mapParams?: string; }>; minParams?: number; returnType: FunctionReturnType; diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/__tests__/callbacks.test.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/__tests__/callbacks.test.ts index e87fbd55c2c50..16898cc737e58 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/__tests__/callbacks.test.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/__tests__/callbacks.test.ts @@ -33,7 +33,7 @@ describe('FROM', () => { test('loads fields from JOIN index', async () => { const { validate, callbacks } = await setup(); - await validate('FROM index1 | JOIN index2 ON field1 | LIMIT 123'); + await validate('FROM index1 | LOOKUP JOIN index2 ON field1 | LIMIT 123'); expect((callbacks.getColumnsFor as any).mock.calls.length).toBe(1); @@ -47,7 +47,7 @@ describe('FROM', () => { const { validate, callbacks } = await setup(); await validate( - 'FROM index1, index2, index3 | JOIN index4 ON field1 | KEEP abc | JOIN index5 ON field2 | LIMIT 123' + 'FROM index1, index2, index3 | LOOKUP JOIN index4 ON field1 | KEEP abc | LOOKUP JOIN index5 ON field2 | LIMIT 123' ); expect((callbacks.getColumnsFor as any).mock.calls.length).toBe(1); diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 0994b6abd3e09..66a7d8c98a5d9 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -2267,28 +2267,28 @@ { "query": "from index | project ", "error": [ - "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}" + "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}" ], "warning": [] }, { "query": "from index | project textField, doubleField, dateField", "error": [ - "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}" + "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}" ], "warning": [] }, { "query": "from index | PROJECT textField, doubleField, dateField", "error": [ - "SyntaxError: mismatched input 'PROJECT' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}" + "SyntaxError: mismatched input 'PROJECT' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}" ], "warning": [] }, { "query": "from index | project missingField, doubleField, dateField", "error": [ - "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}" + "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}" ], "warning": [] }, diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index 37b100ba3eaa8..a7172f50ce803 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -537,16 +537,16 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from index | keep `any#Char$Field`', []); testErrorsAndWarnings('from index | project ', [ - "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}", + "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}", ]); testErrorsAndWarnings('from index | project textField, doubleField, dateField', [ - "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}", + "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}", ]); testErrorsAndWarnings('from index | PROJECT textField, doubleField, dateField', [ - "SyntaxError: mismatched input 'PROJECT' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}", + "SyntaxError: mismatched input 'PROJECT' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}", ]); testErrorsAndWarnings('from index | project missingField, doubleField, dateField', [ - "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where'}", + "SyntaxError: mismatched input 'project' expecting {'dissect', 'drop', 'enrich', 'eval', 'grok', 'keep', 'limit', 'mv_expand', 'rename', 'sort', 'stats', 'where', 'lookup'}", ]); testErrorsAndWarnings('from index | keep k*', []); testErrorsAndWarnings('from index | keep *Field', []); diff --git a/src/platform/packages/shared/kbn-monaco/src/esql/lib/esql_theme.ts b/src/platform/packages/shared/kbn-monaco/src/esql/lib/esql_theme.ts index f5e65284844ec..aebe61ba92904 100644 --- a/src/platform/packages/shared/kbn-monaco/src/esql/lib/esql_theme.ts +++ b/src/platform/packages/shared/kbn-monaco/src/esql/lib/esql_theme.ts @@ -73,8 +73,6 @@ export const buildESQLTheme = ({ 'as', 'limit', 'dev_lookup', - 'dev_join_lookup', - 'dev_join', 'dev_join_full', 'dev_join_left', 'dev_join_right', @@ -86,6 +84,8 @@ export const buildESQLTheme = ({ 'asc', 'desc', 'nulls_order', + 'join_lookup', + 'join', ], euiTheme.colors.accent, true // isBold diff --git a/src/platform/packages/shared/kbn-router-to-openapispec/src/oas_converter/zod/lib.ts b/src/platform/packages/shared/kbn-router-to-openapispec/src/oas_converter/zod/lib.ts index 7d247ace892b5..f83a37b45c6be 100644 --- a/src/platform/packages/shared/kbn-router-to-openapispec/src/oas_converter/zod/lib.ts +++ b/src/platform/packages/shared/kbn-router-to-openapispec/src/oas_converter/zod/lib.ts @@ -8,7 +8,6 @@ */ import { z, isZod } from '@kbn/zod'; -// eslint-disable-next-line import/no-extraneous-dependencies import zodToJsonSchema from 'zod-to-json-schema'; import type { OpenAPIV3 } from 'openapi-types'; diff --git a/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.test.ts b/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.test.ts deleted file mode 100644 index b302c999528ff..0000000000000 --- a/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { z } from '@kbn/zod'; -import { makeZodValidationObject } from './make_zod_validation_object'; -import { noParamsValidationObject } from './validation_objects'; - -describe('makeZodValidationObject', () => { - it('translate path to params', () => { - const schema = z.object({ - path: z.object({}), - }); - - expect(makeZodValidationObject(schema)).toMatchObject({ - params: expect.anything(), - }); - }); - - it('makes all object types strict', () => { - const schema = z.object({ - path: z.object({}), - query: z.object({}), - body: z.string(), - }); - - const pathStrictSpy = jest.spyOn(schema.shape.path, 'strict'); - const queryStrictSpy = jest.spyOn(schema.shape.query, 'strict'); - - expect(makeZodValidationObject(schema)).toEqual({ - params: pathStrictSpy.mock.results[0].value, - query: queryStrictSpy.mock.results[0].value, - body: schema.shape.body, - }); - }); - - it('sets key to strict empty if schema is missing key', () => { - const schema = z.object({}); - - expect(makeZodValidationObject(schema)).toStrictEqual({ - params: noParamsValidationObject.params, - query: noParamsValidationObject.query, - body: noParamsValidationObject.body, - }); - }); -}); diff --git a/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.ts b/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.ts index 23d50e5bdb25c..5e9b9eb621f15 100644 --- a/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.ts +++ b/src/platform/packages/shared/kbn-server-route-repository/src/make_zod_validation_object.ts @@ -7,7 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { z, ZodObject } from '@kbn/zod'; +import { z } from '@kbn/zod'; +import { DeepStrict } from '@kbn/zod-helpers'; import { ZodParamsObject } from '@kbn/server-route-repository-utils'; import { noParamsValidationObject } from './validation_objects'; @@ -20,9 +21,5 @@ export function makeZodValidationObject(params: ZodParamsObject) { } function asStrict(schema: z.Schema) { - if (schema instanceof ZodObject) { - return schema.strict(); - } else { - return schema; - } + return DeepStrict(schema); } diff --git a/src/platform/packages/shared/kbn-server-route-repository/src/register_routes.ts b/src/platform/packages/shared/kbn-server-route-repository/src/register_routes.ts index 90c4f42b9ce44..0bd051e6b62df 100644 --- a/src/platform/packages/shared/kbn-server-route-repository/src/register_routes.ts +++ b/src/platform/packages/shared/kbn-server-route-repository/src/register_routes.ts @@ -26,6 +26,7 @@ import { observableIntoEventSourceStream } from '@kbn/sse-utils-server'; import { isZod } from '@kbn/zod'; import { merge, omit } from 'lodash'; import { Observable, isObservable } from 'rxjs'; +import { assertAllParsableSchemas } from '@kbn/zod-helpers'; import { makeZodValidationObject } from './make_zod_validation_object'; import { validateAndDecodeParams } from './validate_and_decode_params'; import { noParamsValidationObject, passThroughValidationObject } from './validation_objects'; @@ -61,6 +62,25 @@ export function registerRoutes>({ const { method, pathname, version } = parseEndpoint(endpoint); + if (isZod(params)) { + const dangerousSchemas = assertAllParsableSchemas(params); + if (dangerousSchemas.size > 0) { + for (const { key, schema } of dangerousSchemas) { + const typeName = schema._def.typeName; + + if (typeName === 'ZodEffects') { + logger.warn( + `Warning for ${endpoint}: schema ${typeName} at ${key} has transforming effects and could lead to unexpected behaviour` + ); + } else { + logger.warn( + `Warning for ${endpoint}: schema ${typeName} at ${key} is not inspectable and could lead to runtime exceptions, convert it to a support schema` + ); + } + } + } + } + const wrappedHandler = async ( context: RequestHandlerContext, request: KibanaRequest, diff --git a/src/platform/packages/shared/kbn-server-route-repository/tsconfig.json b/src/platform/packages/shared/kbn-server-route-repository/tsconfig.json index 8a493c3b83722..5a7183333db1f 100644 --- a/src/platform/packages/shared/kbn-server-route-repository/tsconfig.json +++ b/src/platform/packages/shared/kbn-server-route-repository/tsconfig.json @@ -23,6 +23,7 @@ "@kbn/zod", "@kbn/sse-utils-server", "@kbn/sse-utils", + "@kbn/zod-helpers", ], "exclude": [ "target/**/*", diff --git a/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/__snapshots__/use_comparison_css.test.ts.snap b/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/__snapshots__/use_comparison_css.test.ts.snap index 78ee59595a411..2ae6bb1fcd407 100644 --- a/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/__snapshots__/use_comparison_css.test.ts.snap +++ b/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/__snapshots__/use_comparison_css.test.ts.snap @@ -3,7 +3,7 @@ exports[`useComparisonCss should render with basic diff mode and diff decorations 1`] = ` Object { "map": undefined, - "name": "1qc86qa", + "name": "14zju9t", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -15,7 +15,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -63,7 +63,7 @@ Object { exports[`useComparisonCss should render with basic diff mode and no diff decorations 1`] = ` Object { "map": undefined, - "name": "1qc86qa", + "name": "14zju9t", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -75,7 +75,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -123,7 +123,7 @@ Object { exports[`useComparisonCss should render with chars diff mode and diff decorations 1`] = ` Object { "map": undefined, - "name": "vd39me", + "name": "15v0erc", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -135,7 +135,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -173,7 +173,7 @@ Object { exports[`useComparisonCss should render with chars diff mode and no diff decorations 1`] = ` Object { "map": undefined, - "name": "1ylxgdl", + "name": "z8l3vw", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -185,7 +185,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -215,7 +215,7 @@ Object { exports[`useComparisonCss should render with lines diff mode and diff decorations 1`] = ` Object { "map": undefined, - "name": "1nu19hw", + "name": "jvfp05", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -227,7 +227,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -293,7 +293,7 @@ Object { exports[`useComparisonCss should render with lines diff mode and no diff decorations 1`] = ` Object { "map": undefined, - "name": "1mthx0u", + "name": "trn277", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -305,7 +305,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -341,7 +341,7 @@ Object { exports[`useComparisonCss should render with no diff mode and no diff decorations 1`] = ` Object { "map": undefined, - "name": "1ylxgdl", + "name": "z8l3vw", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -353,7 +353,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -383,7 +383,7 @@ Object { exports[`useComparisonCss should render with words diff mode and diff decorations 1`] = ` Object { "map": undefined, - "name": "vd39me", + "name": "15v0erc", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -395,7 +395,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } @@ -433,7 +433,7 @@ Object { exports[`useComparisonCss should render with words diff mode and no diff decorations 1`] = ` Object { "map": undefined, - "name": "1ylxgdl", + "name": "z8l3vw", "next": undefined, "styles": " .unifiedDataTable__cellValue { @@ -445,7 +445,7 @@ Object { } .unifiedDataTable__comparisonBaseDocCell { - background-color: rgba(211,218,230,0.2); + background-color: #f7f8fc; } diff --git a/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/use_comparison_css.ts b/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/use_comparison_css.ts index 868f41cb7f4d8..3121a7d8c6fc3 100644 --- a/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/use_comparison_css.ts +++ b/src/platform/packages/shared/kbn-unified-data-table/src/components/compare_documents/hooks/use_comparison_css.ts @@ -7,7 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { useEuiBackgroundColor, useEuiTheme } from '@elastic/eui'; +import { useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { CELL_CLASS } from '../../../utils/get_render_cell_value'; import { DocumentDiffMode } from '../types'; @@ -28,9 +28,9 @@ export const useComparisonCss = ({ showDiffDecorations?: boolean; }) => { const { euiTheme } = useEuiTheme(); - const baseCellBackgroundColor = useEuiBackgroundColor('subdued', { method: 'transparent' }); - const matchSegmentBackgroundColor = useEuiBackgroundColor('success'); - const diffSegmentBackgroundColor = useEuiBackgroundColor('danger'); + const baseCellBackgroundColor = euiTheme.colors.backgroundBaseSubdued; + const matchSegmentBackgroundColor = euiTheme.colors.backgroundBaseSuccess; + const diffSegmentBackgroundColor = euiTheme.colors.backgroundBaseDanger; const indicatorCss = css` position: absolute; diff --git a/src/platform/packages/shared/kbn-unified-data-table/src/table_context.tsx b/src/platform/packages/shared/kbn-unified-data-table/src/table_context.tsx index 138e28b7b9647..204453416b6f8 100644 --- a/src/platform/packages/shared/kbn-unified-data-table/src/table_context.tsx +++ b/src/platform/packages/shared/kbn-unified-data-table/src/table_context.tsx @@ -27,7 +27,6 @@ export interface DataTableContext { isPlainRecord?: boolean; pageIndex: number | undefined; // undefined when the pagination is disabled pageSize: number | undefined; - inTableSearchTerm?: string; } const defaultContext = {} as unknown as DataTableContext; diff --git a/src/platform/packages/shared/kbn-zod-helpers/index.ts b/src/platform/packages/shared/kbn-zod-helpers/index.ts index 65624b7ec6c80..73afabf935d5f 100644 --- a/src/platform/packages/shared/kbn-zod-helpers/index.ts +++ b/src/platform/packages/shared/kbn-zod-helpers/index.ts @@ -17,3 +17,4 @@ export * from './src/safe_parse_result'; export * from './src/stringify_zod_error'; export * from './src/build_route_validation_with_zod'; export * from './src/non_empty_string'; +export * from './src/deep_strict'; diff --git a/src/platform/packages/shared/kbn-zod-helpers/src/deep_strict.test.ts b/src/platform/packages/shared/kbn-zod-helpers/src/deep_strict.test.ts new file mode 100644 index 0000000000000..55bfbfcc80c0a --- /dev/null +++ b/src/platform/packages/shared/kbn-zod-helpers/src/deep_strict.test.ts @@ -0,0 +1,295 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { z } from '@kbn/zod'; +import { DeepStrict } from './deep_strict'; +import { isValidDateMath } from './is_valid_date_math'; + +function wrap(schema: z.Schema) { + const wrappedSchema = DeepStrict(schema); + + function wrapInner() { + return { + passes: (value: unknown) => { + wrappedSchema.parse(value); + return wrapInner(); + }, + fails: (value: unknown) => { + const result = wrappedSchema.safeParse(value); + if (result.success) { + throw new Error(`Expected value to fail validation`); + } + return wrapInner(); + }, + }; + } + + return wrapInner(); +} + +describe('DeepStrict', () => { + const timeWindowRt = z.union([ + z.object({ duration: z.string() }), + z.object({ start_time: z.string().superRefine(isValidDateMath) }), + ]); + + const metricQueryRt = z.union([ + z.object({ + avg_over_time: z.intersection( + z.object({ + field: z.string(), + }), + z + .object({ + range: z.string().optional(), + }) + .optional() + ), + }), + z.object({ + count_over_time: z.strictObject({}), + }), + ]); + + const metricExpressionRt = z.object({ + expression: z.string(), + }); + + const metricRt = z.intersection( + z + .object({ + record: z.boolean().optional(), + }) + .optional(), + z.union([metricQueryRt, metricExpressionRt]) + ); + + const metricContainerRt = z.record(z.string(), metricRt); + + const groupingRt = z.object({ + by: z.record( + z.string(), + z.object({ + field: z.string(), + }) + ), + limit: z.number(), + }); + + const queryRt = z.intersection( + z.union([groupingRt, z.object({})]), + z.intersection( + z.object({ + index: z.union([z.string(), z.array(z.string())]), + metrics: metricContainerRt, + }), + z + .object({ + filter: z.string().optional(), + round: z.string().optional(), + runtime_mappings: z.string().optional(), + query_delay: z.string().optional(), + }) + .optional() + ) + ); + + it('intersection with optional', () => { + wrap( + z + .intersection( + z.object({ foo: z.string() }), + z.object({ bar: z.string().optional() }).optional() + ) + .optional() + ) + .passes({ foo: '' }) + .passes({ foo: '', bar: '' }) + .fails({ foo: '', unknownKey: '' }) + .fails({ foo: '', bar: '', unknownKey: '' }); + }); + + it('object with union types', () => { + wrap( + z.object({ + path: z.union([ + z.object({ serviceName: z.string() }), + z.object({ transactionType: z.string() }), + ]), + }) + ) + .passes({ path: { serviceName: '' } }) + .passes({ path: { transactionType: '' } }) + .fails({ path: { serviceName: '', unknownKey: '' } }) + .fails({ path: { transactionType: '', unknownKey: '' } }) + .fails({ path: { serviceName: '', transactionType: '' } }) + .fails({ path: { serviceName: '' }, unknownKey: '' }); + }); + + it('object with deep optional keys', () => { + wrap( + z.intersection( + z.object({ query: z.object({ bar: z.string() }) }), + z.object({ query: z.object({ _inspect: z.boolean() }).optional() }).optional() + ) + ) + .passes({ query: { bar: '', _inspect: true } }) + .fails({ query: { _inspect: true } }); + }); + + it('complex object', () => { + const fromSchema = z + .object({ + from: z.string().optional(), + }) + .optional(); + + const configSchema = z.object({ + config: z.intersection( + fromSchema, + z.intersection( + z.object({ + alert: z.object({}), + }), + z.union([ + z.object({ + query: queryRt, + }), + z.object({ + queries: z.array(queryRt), + }), + ]) + ) + ), + }); + + wrap( + z.object({ + body: z.intersection(fromSchema, configSchema), + }) + ) + .passes({ + body: { + config: { + alert: {}, + query: { + index: ['apm-*'], + filter: 'processor.event:transaction', + metrics: { + avg_latency_1h: { + avg_over_time: { + field: 'transaction.duration.us', + }, + }, + }, + }, + }, + }, + }) + .fails({ + body: { + config: { + alert: {}, + query: { + index: '', + metrics: { + avg_latency_1h: { + avg_over_time: { + field: '', + range: '', + }, + }, + rate_1h: { + count_over_time: { + field: '', + }, + }, + }, + }, + }, + }, + }); + }); + + it('refinements', () => { + wrap(z.object({ body: timeWindowRt })) + .passes({ body: { duration: '1d' } }) + .passes({ body: { start_time: '2022-05-20T08:10:15.000Z' } }) + .fails({ body: { duration: '1d', start_time: '2022-05-20T08:10:15.000Z' } }) + .fails({ body: { duration: '1d', unknownKey: '' } }) + .fails({ body: { start_time: '2022-05-20T08:10:15.000Z', unknownKey: '' } }) + .fails({ body: { unknownKey: '' } }) + .fails({ body: { start_time: 'invalid' } }) + .fails({ body: { duration: false } }); + }); + + it('arrays', () => { + const schema = z.array(z.object({ foo: z.string() })); + + wrap(schema) + .passes([{ foo: 'bar' }]) + .passes([{ foo: 'baz' }, { foo: 'bar' }]) + .fails([{ foo: 'bar', bar: 'foo' }]); + }); + + it('nested arrays', () => { + wrap( + z.object({ + nestedArray: z.array( + z.object({ + bar: z.string(), + }) + ), + }) + ) + .passes({ + nestedArray: [], + }) + .passes({ + nestedArray: [ + { + bar: 'foo', + }, + ], + }) + .fails({ + nestedArray: [ + { + bar: 'foo', + foo: 'bar', + }, + ], + }); + }); + + it('deals with union types', () => { + const type = z.intersection( + z.object({ + required: z.string(), + }), + z + .object({ + disable: z.union([ + z.boolean(), + z.object({ + except: z.array(z.string()), + }), + ]), + }) + .optional() + ); + + wrap(type).passes({ + required: 'required', + disable: { + except: ['foo'], + }, + }); + }); +}); diff --git a/src/platform/packages/shared/kbn-zod-helpers/src/deep_strict.ts b/src/platform/packages/shared/kbn-zod-helpers/src/deep_strict.ts new file mode 100644 index 0000000000000..d458b301b0299 --- /dev/null +++ b/src/platform/packages/shared/kbn-zod-helpers/src/deep_strict.ts @@ -0,0 +1,322 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { + Schema, + z, + ZodAny, + ZodEffects, + ZodFirstPartySchemaTypes, + ZodFirstPartyTypeKind, + ZodIssueCode, + ZodTypeAny, +} from '@kbn/zod'; +import { difference, isPlainObject, forEach, isArray, castArray } from 'lodash'; + +/** + * These types are not unpackable, they mark a property as handled, + * but any nested keys will result in runtime failures + */ +const primitiveTypes = [ + ZodFirstPartyTypeKind.ZodString, + ZodFirstPartyTypeKind.ZodBoolean, + ZodFirstPartyTypeKind.ZodNull, + ZodFirstPartyTypeKind.ZodUndefined, + ZodFirstPartyTypeKind.ZodNumber, + ZodFirstPartyTypeKind.ZodNaN, + ZodFirstPartyTypeKind.ZodLiteral, + ZodFirstPartyTypeKind.ZodEnum, + ZodFirstPartyTypeKind.ZodNativeEnum, +] as const; + +/** + * any() and unknown() will result in warnings, + * as we don't really know what to do with them + * and they can lead to unexpected behavior at + * runtime, such as failing on excess keys for + * objects + */ +const dangerousTypes = [ZodFirstPartyTypeKind.ZodAny, ZodFirstPartyTypeKind.ZodUnknown] as const; + +const typeNames = [ + ZodFirstPartyTypeKind.ZodObject, + ZodFirstPartyTypeKind.ZodArray, + ZodFirstPartyTypeKind.ZodUnion, + ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + ZodFirstPartyTypeKind.ZodIntersection, + ZodFirstPartyTypeKind.ZodOptional, + ZodFirstPartyTypeKind.ZodEffects, + ZodFirstPartyTypeKind.ZodRecord, + ZodFirstPartyTypeKind.ZodDefault, + ZodFirstPartyTypeKind.ZodLazy, + ZodFirstPartyTypeKind.ZodNullable, + ...primitiveTypes, + ...dangerousTypes, +] as const; + +type ParsableType = (typeof typeNames)[number]; + +type PrimitiveParsableType = (typeof primitiveTypes)[number]; +type DangerousParsableType = (typeof dangerousTypes)[number]; + +type ParsableSchema = Extract; +type PrimitiveParsableSchema = Extract< + ZodFirstPartySchemaTypes, + { _def: { typeName: PrimitiveParsableType } } +>; + +type DangerousParsableSchema = + | Extract + | ZodEffects; + +function getTypeName(schema: z.Schema): string | undefined { + const typeName = + 'typeName' in schema._def && typeof schema._def.typeName === 'string' + ? schema._def.typeName + : undefined; + + return typeName; +} + +function isParsableSchema(schema: z.Schema): schema is ParsableSchema { + const typeName = getTypeName(schema); + return !!typeName && typeNames.includes(typeName as ParsableType); +} + +function assertIsParsableSchema(schema: z.Schema, key: string): asserts schema is ParsableSchema { + if (isParsableSchema(schema)) { + return; + } + + throw new Error( + `Unsupported schema at ${key}: ${ + 'typeName' in schema._def ? schema._def.typeName : 'unknown type' + }` + ); +} + +function isPrimitiveParsableSchema(schema: z.Schema): schema is PrimitiveParsableSchema { + const typeName = getTypeName(schema); + return !!typeName && primitiveTypes.includes(typeName as PrimitiveParsableType); +} + +function isDangerousParsableSchema( + schema: z.Schema +): schema is Exclude> { + const typeName = getTypeName(schema); + return !!typeName && dangerousTypes.includes(typeName as DangerousParsableType); +} +/** + * Asserts that the schema is recursively parsable (ie, it is + * composed entirely of parsable schemas) + */ +export function assertAllParsableSchemas( + outerSchema: z.Schema +): Set<{ key: string; schema: DangerousParsableSchema }> { + // track processed schemas, to prevent self-referencing schemas + // instantiated with z.lazy() to create recursion errors + const processed: Set = new Set(); + // track dangerous schemas and their path + const dangerous: Set<{ key: string; schema: DangerousParsableSchema }> = new Set(); + + innerAssertAllParsableSchemas(outerSchema, ''); + + return dangerous; + + function innerAssertAllParsableSchemas(schema: z.Schema, key: string): void { + assertIsParsableSchema(schema, key); + + if (processed.has(schema)) { + return; + } + + processed.add(schema); + + if (isPrimitiveParsableSchema(schema)) { + return; + } + + if (isDangerousParsableSchema(schema)) { + dangerous.add({ key, schema }); + return; + } + + const def = schema._def; + + switch (def.typeName) { + case ZodFirstPartyTypeKind.ZodObject: + return Object.entries(def.shape()).forEach(([prop, innerSchema]) => + innerAssertAllParsableSchemas(innerSchema as z.Schema, key ? `${key}.${prop}` : prop) + ); + case ZodFirstPartyTypeKind.ZodOptional: + return innerAssertAllParsableSchemas(def.innerType, key); + case ZodFirstPartyTypeKind.ZodArray: + return innerAssertAllParsableSchemas(def.type, key); + + case ZodFirstPartyTypeKind.ZodUnion: + case ZodFirstPartyTypeKind.ZodDiscriminatedUnion: + const schemas: z.Schema[] = def.options; + return schemas.forEach((innerSchema) => innerAssertAllParsableSchemas(innerSchema, key)); + + case z.ZodFirstPartyTypeKind.ZodIntersection: + innerAssertAllParsableSchemas(def.left, key); + innerAssertAllParsableSchemas(def.right, key); + return; + + case z.ZodFirstPartyTypeKind.ZodEffects: + if (def.effect.type === 'transform') { + dangerous.add({ key, schema: schema as ZodEffects }); + } + return innerAssertAllParsableSchemas(def.schema, key); + + case z.ZodFirstPartyTypeKind.ZodRecord: + return innerAssertAllParsableSchemas(def.valueType, key); + + case ZodFirstPartyTypeKind.ZodDefault: + return innerAssertAllParsableSchemas(def.innerType, key); + + case ZodFirstPartyTypeKind.ZodLazy: + return innerAssertAllParsableSchemas(def.getter(), key); + + case ZodFirstPartyTypeKind.ZodNullable: + return innerAssertAllParsableSchemas(def.innerType, key); + } + } +} + +function getHandlingSchemas(schema: z.Schema, key: string, value: object): z.Schema[] { + assertIsParsableSchema(schema, key); + + if (isPrimitiveParsableSchema(schema) || isDangerousParsableSchema(schema)) { + return []; + } + + const def = schema._def; + + switch (def.typeName) { + case ZodFirstPartyTypeKind.ZodObject: + return [def.shape()[key] as z.Schema]; + case ZodFirstPartyTypeKind.ZodOptional: + return getHandlingSchemas(def.innerType, key, value); + case ZodFirstPartyTypeKind.ZodArray: + return [def.type as z.Schema]; + + case ZodFirstPartyTypeKind.ZodUnion: + case ZodFirstPartyTypeKind.ZodDiscriminatedUnion: + const types: z.Schema[] = def.options; + // for union types, we should only return the first matching union type + // to make sure unmatching union types don't mark a key as handled + const matched = types.find((type) => type.safeParse(value).success); + return matched ? getHandlingSchemas(matched, key, value) : []; + + case z.ZodFirstPartyTypeKind.ZodIntersection: + return [ + ...getHandlingSchemas(def.left, key, value), + ...getHandlingSchemas(def.right, key, value), + ]; + + case z.ZodFirstPartyTypeKind.ZodEffects: + return [def.schema]; + + case z.ZodFirstPartyTypeKind.ZodRecord: + return [def.valueType]; + + case ZodFirstPartyTypeKind.ZodDefault: + return [def.innerType]; + + case ZodFirstPartyTypeKind.ZodLazy: + return [def.getter()]; + + case ZodFirstPartyTypeKind.ZodNullable: + return [def.innerType]; + } +} + +function getHandledKeys>( + type: z.Schema, + object: T, + prefix: string = '' +): { handled: Set; all: Set } { + const keys: { + handled: Set; + all: Set; + } = { + handled: new Set(), + all: new Set(), + }; + + forEach(object, (value, key) => { + const ownPrefix = prefix ? `${prefix}.${key}` : key; + keys.all.add(ownPrefix); + + const handlingTypes = getHandlingSchemas(type, key, object).filter(Boolean); + + if (handlingTypes.length) { + keys.handled.add(ownPrefix); + } + + const processObject = (typeForObject: z.Schema, objectToProcess: Record) => { + const nextKeys = getHandledKeys(typeForObject, objectToProcess, ownPrefix); + nextKeys.all.forEach((k) => keys.all.add(k)); + nextKeys.handled.forEach((k) => keys.handled.add(k)); + }; + + if (isPlainObject(value)) { + handlingTypes.forEach((typeAtIndex) => { + processObject(typeAtIndex, value as Record); + }); + } + + if (isArray(value)) { + handlingTypes.forEach((typeAtIndex) => { + if ( + !isParsableSchema(typeAtIndex) || + typeAtIndex._def.typeName !== ZodFirstPartyTypeKind.ZodArray + ) { + return; + } + + const innerType = typeAtIndex._def.type; + + castArray(value).forEach((valueAtIndex) => { + if (isPlainObject(valueAtIndex)) { + processObject(innerType, valueAtIndex as Record); + } + }); + }); + } + }); + + return keys; +} + +export function DeepStrict(schema: TSchema) { + assertAllParsableSchemas(schema); + + /** + * We use preprocess and not superRefine because unknown keys are + * stripped before the latter is called, meaning we can no longer + * validate. There is a catch here: if any types do any transformation, + * we might match the wrong union type. + */ + return z.preprocess((value, context) => { + const keys = getHandledKeys(schema, value as Record); + + const excessKeys = difference([...keys.all], [...keys.handled]); + + if (excessKeys.length) { + context.addIssue({ + code: ZodIssueCode.unrecognized_keys, + keys: excessKeys, + message: `Excess keys are not allowed`, + }); + } + return value; + }, schema); +} diff --git a/src/platform/packages/private/serverless/project_switcher/jest.config.js b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts similarity index 74% rename from src/platform/packages/private/serverless/project_switcher/jest.config.js rename to src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts index 17c8a482cb94b..b0bbf12bb85fe 100644 --- a/src/platform/packages/private/serverless/project_switcher/jest.config.js +++ b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/index.ts @@ -7,8 +7,5 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -module.exports = { - preset: '@kbn/test', - rootDir: '../../../../../..', - roots: ['/src/platform/packages/private/serverless/project_switcher'], -}; +export { customThresholdParamsSchema } from './latest'; +export { customThresholdParamsSchema as customThresholdParamsSchemaV1 } from './v1'; diff --git a/src/platform/packages/private/serverless/project_switcher/index.ts b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.ts similarity index 73% rename from src/platform/packages/private/serverless/project_switcher/index.ts rename to src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.ts index ac07a62f5c284..f278309c22b03 100644 --- a/src/platform/packages/private/serverless/project_switcher/index.ts +++ b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/latest.ts @@ -7,6 +7,4 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export type { ProjectSwitcherProps, KibanaDependencies } from './src'; - -export { ProjectSwitcher, ProjectSwitcherKibanaProvider, ProjectSwitcherProvider } from './src'; +export * from './v1'; diff --git a/src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts new file mode 100644 index 0000000000000..dc7034077b1d0 --- /dev/null +++ b/src/platform/packages/shared/response-ops/rule_params/custom_threshold/v1.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { schema } from '@kbn/config-schema'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { dataViewSpecSchema } from '../common'; +import { oneOfLiterals, validateKQLStringFilter, LEGACY_COMPARATORS } from '../common/utils'; + +const allowedAggregators = [ + 'avg', + 'sum', + 'min', + 'max', + 'cardinality', + 'rate', + 'p95', + 'p99', + 'last_value', +]; + +const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); + +const searchConfigSchema = schema.object({ + index: schema.oneOf([schema.string(), dataViewSpecSchema]), + query: schema.object({ + language: schema.string(), + query: schema.string({ + validate: validateKQLStringFilter, + }), + }), + filter: schema.maybe( + schema.arrayOf( + schema.object({ + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + meta: schema.recordOf(schema.string(), schema.any()), + }) + ) + ), +}); + +const customCriterion = schema.object({ + threshold: schema.arrayOf(schema.number()), + comparator: oneOfLiterals(comparators), + timeUnit: schema.string(), + timeSize: schema.number(), + aggType: schema.maybe(schema.literal('custom')), + metric: schema.never(), + metrics: schema.arrayOf( + schema.oneOf([ + schema.object({ + name: schema.string(), + aggType: oneOfLiterals(allowedAggregators), + field: schema.string(), + filter: schema.never(), + }), + schema.object({ + name: schema.string(), + aggType: schema.literal('count'), + filter: schema.maybe( + schema.string({ + validate: validateKQLStringFilter, + }) + ), + field: schema.never(), + }), + ]) + ), + equation: schema.maybe(schema.string()), + label: schema.maybe(schema.string()), +}); + +export const customThresholdParamsSchema = schema.object( + { + criteria: schema.arrayOf(customCriterion), + groupBy: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), + alertOnNoData: schema.maybe(schema.boolean()), + alertOnGroupDisappear: schema.maybe(schema.boolean()), + searchConfiguration: searchConfigSchema, + }, + { unknowns: 'allow' } +); diff --git a/src/platform/packages/shared/shared-ux/code_editor/impl/package.json b/src/platform/packages/shared/shared-ux/code_editor/impl/package.json index e831aa7ace817..e2def46267623 100644 --- a/src/platform/packages/shared/shared-ux/code_editor/impl/package.json +++ b/src/platform/packages/shared/shared-ux/code_editor/impl/package.json @@ -3,5 +3,7 @@ "private": true, "version": "1.0.0", "license": "Elastic License 2.0 OR AGPL-3.0-only OR SSPL-1.0", - "sideEffects": false -} + "sideEffects": [ + "./register_languages.ts" + ] +} \ No newline at end of file diff --git a/src/platform/plugins/private/links/public/actions/add_links_panel_action.ts b/src/platform/plugins/private/links/public/actions/add_links_panel_action.ts new file mode 100644 index 0000000000000..95c280d14e544 --- /dev/null +++ b/src/platform/plugins/private/links/public/actions/add_links_panel_action.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { ADD_PANEL_ANNOTATION_GROUP } from '@kbn/embeddable-plugin/public'; +import type { ActionDefinition } from '@kbn/ui-actions-plugin/public/actions'; +import { apiIsPresentationContainer } from '@kbn/presentation-containers'; +import { + apiPublishesDescription, + apiPublishesTitle, + apiPublishesSavedObjectId, +} from '@kbn/presentation-publishing'; +import type { LinksParentApi } from '../types'; +import { APP_ICON, APP_NAME, CONTENT_ID } from '../../common'; +import { ADD_LINKS_PANEL_ACTION_ID } from './constants'; +import { openEditorFlyout } from '../editor/open_editor_flyout'; + +export const isParentApiCompatible = (parentApi: unknown): parentApi is LinksParentApi => + apiIsPresentationContainer(parentApi) && + apiPublishesSavedObjectId(parentApi) && + apiPublishesTitle(parentApi) && + apiPublishesDescription(parentApi); + +export const addLinksPanelAction: ActionDefinition = { + id: ADD_LINKS_PANEL_ACTION_ID, + getIconType: () => APP_ICON, + order: 10, + isCompatible: async ({ embeddable }) => { + return isParentApiCompatible(embeddable); + }, + execute: async ({ embeddable }) => { + if (!isParentApiCompatible(embeddable)) throw new IncompatibleActionError(); + const runtimeState = await openEditorFlyout({ + parentDashboard: embeddable, + }); + if (!runtimeState) return; + + await embeddable.addNewPanel({ + panelType: CONTENT_ID, + initialState: runtimeState, + }); + }, + grouping: [ADD_PANEL_ANNOTATION_GROUP], + getDisplayName: () => APP_NAME, +}; diff --git a/src/platform/plugins/private/links/public/actions/compatibility_check.ts b/src/platform/plugins/private/links/public/actions/compatibility_check.ts deleted file mode 100644 index ce81921939061..0000000000000 --- a/src/platform/plugins/private/links/public/actions/compatibility_check.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { apiIsPresentationContainer } from '@kbn/presentation-containers'; -import { - apiPublishesDescription, - apiPublishesTitle, - apiPublishesSavedObjectId, -} from '@kbn/presentation-publishing'; -import { LinksParentApi } from '../types'; - -export const isParentApiCompatible = (parentApi: unknown): parentApi is LinksParentApi => - apiIsPresentationContainer(parentApi) && - apiPublishesSavedObjectId(parentApi) && - apiPublishesTitle(parentApi) && - apiPublishesDescription(parentApi); diff --git a/src/platform/packages/private/serverless/project_switcher/src/index.ts b/src/platform/plugins/private/links/public/actions/constants.ts similarity index 69% rename from src/platform/packages/private/serverless/project_switcher/src/index.ts rename to src/platform/plugins/private/links/public/actions/constants.ts index 8cdb5d8909b33..f18e92cc54ada 100644 --- a/src/platform/packages/private/serverless/project_switcher/src/index.ts +++ b/src/platform/plugins/private/links/public/actions/constants.ts @@ -7,7 +7,4 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -export type { KibanaDependencies, ProjectSwitcherProps } from './types'; - -export { ProjectSwitcher } from './switcher'; -export { ProjectSwitcherKibanaProvider, ProjectSwitcherProvider } from './services'; +export const ADD_LINKS_PANEL_ACTION_ID = 'create_links_panel'; diff --git a/src/platform/plugins/private/links/public/actions/create_links_panel_action.ts b/src/platform/plugins/private/links/public/actions/create_links_panel_action.ts deleted file mode 100644 index 3fa64383eb797..0000000000000 --- a/src/platform/plugins/private/links/public/actions/create_links_panel_action.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { EmbeddableApiContext } from '@kbn/presentation-publishing'; -import { ADD_PANEL_TRIGGER, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { ADD_PANEL_ANNOTATION_GROUP } from '@kbn/embeddable-plugin/public'; -import { APP_ICON, APP_NAME, CONTENT_ID } from '../../common'; -import { uiActions } from '../services/kibana_services'; - -const ADD_LINKS_PANEL_ACTION_ID = 'create_links_panel'; - -export const registerCreateLinksPanelAction = () => { - uiActions.registerAction({ - id: ADD_LINKS_PANEL_ACTION_ID, - getIconType: () => APP_ICON, - order: 10, - isCompatible: async ({ embeddable }) => { - const { isParentApiCompatible } = await import('./compatibility_check'); - return isParentApiCompatible(embeddable); - }, - execute: async ({ embeddable }) => { - const { isParentApiCompatible } = await import('./compatibility_check'); - if (!isParentApiCompatible(embeddable)) throw new IncompatibleActionError(); - const { openEditorFlyout } = await import('../editor/open_editor_flyout'); - const runtimeState = await openEditorFlyout({ - parentDashboard: embeddable, - }); - if (!runtimeState) return; - - await embeddable.addNewPanel({ - panelType: CONTENT_ID, - initialState: runtimeState, - }); - }, - grouping: [ADD_PANEL_ANNOTATION_GROUP], - getDisplayName: () => APP_NAME, - }); - uiActions.attachAction(ADD_PANEL_TRIGGER, ADD_LINKS_PANEL_ACTION_ID); -}; diff --git a/src/platform/plugins/private/links/public/components/links_strings.ts b/src/platform/plugins/private/links/public/components/links_strings.ts index 73d0eddf94b81..c990120b4d679 100644 --- a/src/platform/plugins/private/links/public/components/links_strings.ts +++ b/src/platform/plugins/private/links/public/components/links_strings.ts @@ -10,10 +10,6 @@ import { i18n } from '@kbn/i18n'; export const LinksStrings = { - getDescription: () => - i18n.translate('links.description', { - defaultMessage: 'Use links to navigate to commonly used dashboards and websites.', - }), embeddable: { getUnsupportedLinkTypeError: () => i18n.translate('links.embeddable.unsupportedLinkTypeError', { diff --git a/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx b/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx index 86f06f6b475b4..43d50920fb9a0 100644 --- a/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx +++ b/src/platform/plugins/private/links/public/embeddable/links_embeddable.tsx @@ -49,7 +49,7 @@ import { linksSerializeStateIsByReference, } from '../lib/deserialize_from_library'; import { serializeLinksAttributes } from '../lib/serialize_attributes'; -import { isParentApiCompatible } from '../actions/compatibility_check'; +import { isParentApiCompatible } from '../actions/add_links_panel_action'; export const LinksContext = createContext(null); diff --git a/src/platform/plugins/private/links/public/plugin.ts b/src/platform/plugins/private/links/public/plugin.ts index 05b9faf59c7b8..646812da51c80 100644 --- a/src/platform/plugins/private/links/public/plugin.ts +++ b/src/platform/plugins/private/links/public/plugin.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { i18n } from '@kbn/i18n'; import { ContentManagementPublicSetup, ContentManagementPublicStart, @@ -23,14 +24,14 @@ import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; import { VisualizationsSetup } from '@kbn/visualizations-plugin/public'; import { UiActionsPublicStart } from '@kbn/ui-actions-plugin/public/plugin'; +import { ADD_PANEL_TRIGGER } from '@kbn/ui-actions-plugin/public'; import { LinksRuntimeState } from './types'; import { APP_ICON, APP_NAME, CONTENT_ID, LATEST_VERSION } from '../common'; import { LinksCrudTypes } from '../common/content_management'; -import { LinksStrings } from './components/links_strings'; import { getLinksClient } from './content_management/links_content_management_client'; -import { setKibanaServices, untilPluginStartServicesReady } from './services/kibana_services'; -import { registerCreateLinksPanelAction } from './actions/create_links_panel_action'; -import { deserializeLinksSavedObject } from './lib/deserialize_from_library'; +import { setKibanaServices } from './services/kibana_services'; +import { ADD_LINKS_PANEL_ACTION_ID } from './actions/constants'; + export interface LinksSetupDependencies { embeddable: EmbeddableSetup; visualizations: VisualizationsSetup; @@ -63,6 +64,7 @@ export class LinksPlugin plugins.embeddable.registerAddFromLibraryType({ onAdd: async (container, savedObject) => { + const { deserializeLinksSavedObject } = await import('./lib/deserialize_from_library'); const initialState = await deserializeLinksSavedObject(savedObject); container.addNewPanel({ panelType: CONTENT_ID, @@ -84,7 +86,9 @@ export class LinksPlugin name: CONTENT_ID, title: APP_NAME, icon: APP_ICON, - description: LinksStrings.getDescription(), + description: i18n.translate('links.description', { + defaultMessage: 'Use links to navigate to commonly used dashboards and websites.', + }), stage: 'production', appExtensions: { visualizations: { @@ -100,7 +104,11 @@ export class LinksPlugin title, editor: { onEdit: async (savedObjectId: string) => { - const { openEditorFlyout } = await import('./editor/open_editor_flyout'); + const [{ openEditorFlyout }, { deserializeLinksSavedObject }] = + await Promise.all([ + import('./editor/open_editor_flyout'), + import('./lib/deserialize_from_library'), + ]); const linksSavedObject = await getLinksClient().get(savedObjectId); const initialState = await deserializeLinksSavedObject(linksSavedObject.item); await openEditorFlyout({ initialState }); @@ -122,20 +130,26 @@ export class LinksPlugin public start(core: CoreStart, plugins: LinksStartDependencies) { setKibanaServices(core, plugins); - untilPluginStartServicesReady().then(() => { - registerCreateLinksPanelAction(); - plugins.dashboard.registerDashboardPanelPlacementSetting( - CONTENT_ID, - async (runtimeState?: LinksRuntimeState) => { - if (!runtimeState) return {}; - const isHorizontal = runtimeState.layout === 'horizontal'; - const width = isHorizontal ? DASHBOARD_GRID_COLUMN_COUNT : 8; - const height = isHorizontal ? 4 : (runtimeState.links?.length ?? 1 * 3) + 4; - return { width, height, strategy: PanelPlacementStrategy.placeAtTop }; - } - ); - }); + plugins.uiActions.addTriggerActionAsync( + ADD_PANEL_TRIGGER, + ADD_LINKS_PANEL_ACTION_ID, + async () => { + const { addLinksPanelAction } = await import('./actions/add_links_panel_action'); + return addLinksPanelAction; + } + ); + + plugins.dashboard.registerDashboardPanelPlacementSetting( + CONTENT_ID, + async (runtimeState?: LinksRuntimeState) => { + if (!runtimeState) return {}; + const isHorizontal = runtimeState.layout === 'horizontal'; + const width = isHorizontal ? DASHBOARD_GRID_COLUMN_COUNT : 8; + const height = isHorizontal ? 4 : (runtimeState.links?.length ?? 1 * 3) + 4; + return { width, height, strategy: PanelPlacementStrategy.placeAtTop }; + } + ); return {}; } diff --git a/src/platform/plugins/shared/charts/public/services/theme/theme.ts b/src/platform/plugins/shared/charts/public/services/theme/theme.ts index 765daf957e74d..775b9cda93b9a 100644 --- a/src/platform/plugins/shared/charts/public/services/theme/theme.ts +++ b/src/platform/plugins/shared/charts/public/services/theme/theme.ts @@ -11,8 +11,7 @@ import { useEffect, useRef, useState } from 'react'; import { Observable, BehaviorSubject } from 'rxjs'; import { CoreSetup, CoreTheme } from '@kbn/core/public'; -import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; -import { euiThemeVars } from '@kbn/ui-theme'; +import { LIGHT_THEME, PartialTheme, Theme, getChartsTheme } from '@elastic/charts'; export class ThemeService { /** Returns default charts theme */ @@ -104,20 +103,7 @@ export class ThemeService { public init(theme: CoreSetup['theme']) { this.theme$ = theme.theme$; this.theme$.subscribe((newTheme) => { - this._chartsBaseTheme$.next(getChartTheme(newTheme)); + this._chartsBaseTheme$.next(getChartsTheme(newTheme)); }); } } - -// TODO: define these overrides in elastic/charts when Borealis becomes default -function getChartTheme(theme: CoreTheme): Theme { - const chartTheme = theme.darkMode ? DARK_THEME : LIGHT_THEME; - - if (theme.name !== 'amsterdam') { - const backgroundColor = euiThemeVars.euiColorEmptyShade; - chartTheme.background.color = backgroundColor; - chartTheme.background.fallbackColor = backgroundColor; - } - - return chartTheme; -} diff --git a/src/platform/plugins/shared/data/common/search/aggs/metrics/count.test.ts b/src/platform/plugins/shared/data/common/search/aggs/metrics/count.test.ts new file mode 100644 index 0000000000000..1f9c1e57c462a --- /dev/null +++ b/src/platform/plugins/shared/data/common/search/aggs/metrics/count.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import moment from 'moment'; +import { IMetricAggConfig } from './metric_agg_type'; +import { getCountMetricAgg } from './count'; + +function makeAgg(emptyAsNull: boolean = false, timeShift?: moment.Duration): IMetricAggConfig { + return { + getTimeShift() { + return timeShift; + }, + params: { + emptyAsNull, + }, + } as IMetricAggConfig; +} + +function getBucket(value: number | undefined, timeShift?: moment.Duration) { + const suffix = timeShift ? `_${timeShift.asMilliseconds()}` : ''; + return { ['doc_count' + suffix]: value }; +} + +describe('Count', () => { + it('should return the value for a non-shifted bucket', () => { + const agg = getCountMetricAgg(); + expect(agg.getValue(makeAgg(), getBucket(1000))).toBe(1000); + }); + + it('should return the value for a shifted bucket', () => { + const agg = getCountMetricAgg(); + const shift = moment.duration(1800000); + expect(agg.getValue(makeAgg(false, shift), getBucket(1000, shift))).toBe(1000); + }); + + describe('emptyAsNull', () => { + it('should return null if the value is 0 and the flag is enabled', () => { + const agg = getCountMetricAgg(); + expect(agg.getValue(makeAgg(true), getBucket(0))).toBe(null); + }); + + it('should return null if the value is undefined and the flag is enabled', () => { + const agg = getCountMetricAgg(); + expect(agg.getValue(makeAgg(true), getBucket(undefined))).toBe(null); + }); + + it('should return null if the value is 0 and the flag is enabled on a shifted count', () => { + const agg = getCountMetricAgg(); + const shift = moment.duration(1800000); + expect(agg.getValue(makeAgg(true, shift), getBucket(0, shift))).toBe(null); + }); + + it('should return null if the value is undefined and the flag is enabled on a shifted count', () => { + const agg = getCountMetricAgg(); + const shift = moment.duration(1800000); + expect(agg.getValue(makeAgg(true, shift), getBucket(undefined, shift))).toBe(null); + }); + + it('should return 0 if the value is 0 and the flag is disabled', () => { + const agg = getCountMetricAgg(); + expect(agg.getValue(makeAgg(false), getBucket(0))).toBe(0); + }); + + it('should return 0 if the value is undefined and the flag is disabled', () => { + const agg = getCountMetricAgg(); + expect(agg.getValue(makeAgg(false), getBucket(undefined))).toBe(0); + }); + + it('should return 0 if the value is 0 and the flag is disabled on a shifted count', () => { + const agg = getCountMetricAgg(); + const shift = moment.duration(1800000); + expect(agg.getValue(makeAgg(false, shift), getBucket(undefined, shift))).toBe(0); + }); + + it('should return 0 if the value is undefined and the flag is disabled on a shifted count', () => { + const agg = getCountMetricAgg(); + const shift = moment.duration(1800000); + expect(agg.getValue(makeAgg(false, shift), getBucket(undefined, shift))).toBe(0); + }); + }); +}); diff --git a/src/platform/plugins/shared/data/common/search/aggs/metrics/count.ts b/src/platform/plugins/shared/data/common/search/aggs/metrics/count.ts index 232b79266834b..c8dd4f9ecbd44 100644 --- a/src/platform/plugins/shared/data/common/search/aggs/metrics/count.ts +++ b/src/platform/plugins/shared/data/common/search/aggs/metrics/count.ts @@ -48,6 +48,10 @@ export const getCountMetricAgg = () => if (value === 0 && agg.params.emptyAsNull) { return null; } + if (value == null) { + // if the value is undefined, respect the emptyAsNull flag + return agg.params.emptyAsNull ? null : 0; + } return value; }, isScalable() { diff --git a/src/platform/plugins/shared/data/public/query/filter_manager/lib/map_filter.ts b/src/platform/plugins/shared/data/public/query/filter_manager/lib/map_filter.ts index 7d755646b2231..667dd640a2730 100644 --- a/src/platform/plugins/shared/data/public/query/filter_manager/lib/map_filter.ts +++ b/src/platform/plugins/shared/data/public/query/filter_manager/lib/map_filter.ts @@ -75,7 +75,7 @@ export function mapFilter(filter: Filter) { mappedFilter.meta.params = mapped.params; mappedFilter.meta.disabled = Boolean(mappedFilter.meta.disabled); mappedFilter.meta.negate = Boolean(mappedFilter.meta.negate); - mappedFilter.meta.alias = mappedFilter.meta.alias || null; + mappedFilter.meta.alias = mappedFilter.meta.alias; return mappedFilter; } diff --git a/src/platform/plugins/shared/data/public/query/filter_manager/lib/mappers/map_combined.test.ts b/src/platform/plugins/shared/data/public/query/filter_manager/lib/mappers/map_combined.test.ts index 6b4655ca3b9be..efae4fdc80e6b 100644 --- a/src/platform/plugins/shared/data/public/query/filter_manager/lib/mappers/map_combined.test.ts +++ b/src/platform/plugins/shared/data/public/query/filter_manager/lib/mappers/map_combined.test.ts @@ -44,7 +44,7 @@ describe('filter manager utilities', () => { "params": Array [ Object { "meta": Object { - "alias": null, + "alias": undefined, "disabled": false, "index": "logstash-*", "key": "bytes", diff --git a/src/platform/plugins/shared/discover/public/application/main/components/layout/discover_layout.tsx b/src/platform/plugins/shared/discover/public/application/main/components/layout/discover_layout.tsx index f0b9baa29f764..9f9a57449a771 100644 --- a/src/platform/plugins/shared/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/platform/plugins/shared/discover/public/application/main/components/layout/discover_layout.tsx @@ -14,8 +14,8 @@ import { EuiPageBody, EuiPanel, EuiProgress, - useEuiBackgroundColor, EuiDelayRender, + useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; @@ -77,7 +77,8 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { ebtManager, fieldsMetadata, } = useDiscoverServices(); - const pageBackgroundColor = useEuiBackgroundColor('plain'); + const { euiTheme } = useEuiTheme(); + const pageBackgroundColor = euiTheme.colors.backgroundBasePlain; const globalQueryState = data.query.getState(); const { main$ } = stateContainer.dataState.data$; const [query, savedQuery, columns, sort, grid] = useAppStateSelector((state) => [ diff --git a/src/platform/plugins/shared/discover/public/application/main/components/loading_spinner/loading_spinner.tsx b/src/platform/plugins/shared/discover/public/application/main/components/loading_spinner/loading_spinner.tsx index f194bd51575de..9ab4a4ddd6de5 100644 --- a/src/platform/plugins/shared/discover/public/application/main/components/loading_spinner/loading_spinner.tsx +++ b/src/platform/plugins/shared/discover/public/application/main/components/loading_spinner/loading_spinner.tsx @@ -8,26 +8,20 @@ */ import React from 'react'; -import { - EuiLoadingSpinner, - EuiTitle, - EuiSpacer, - useEuiPaddingSize, - useEuiBackgroundColor, -} from '@elastic/eui'; +import { EuiLoadingSpinner, EuiTitle, EuiSpacer, UseEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; export function LoadingSpinner() { - const loadingSpinnerCss = css` + const loadingSpinnerCss = ({ euiTheme }: UseEuiTheme) => css` position: absolute; top: 0; left: 0; width: 100%; height: 100%; text-align: center; - padding: ${useEuiPaddingSize('l')} 0; - background-color: ${useEuiBackgroundColor('plain')}; + padding: ${euiTheme.size.l} 0; + background-color: ${euiTheme.colors.backgroundBasePlain}; z-index: 3; `; diff --git a/src/platform/plugins/shared/telemetry/server/routes/telemetry_usage_stats.ts b/src/platform/plugins/shared/telemetry/server/routes/telemetry_usage_stats.ts index 28198c853e4b5..3adcd00d6a8c7 100644 --- a/src/platform/plugins/shared/telemetry/server/routes/telemetry_usage_stats.ts +++ b/src/platform/plugins/shared/telemetry/server/routes/telemetry_usage_stats.ts @@ -14,7 +14,7 @@ import type { StatsGetterConfig, } from '@kbn/telemetry-collection-manager-plugin/server'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; -import { ApiOperation } from '@kbn/security-plugin-types-server'; +import { ApiOperation } from '@kbn/security-plugin-types-common'; import { RequestHandler } from '@kbn/core-http-server'; import { FetchSnapshotTelemetry } from '../../common/routes'; import { UsageStatsBody, v2 } from '../../common/types'; diff --git a/src/platform/plugins/shared/telemetry/tsconfig.json b/src/platform/plugins/shared/telemetry/tsconfig.json index c43f8a55702c9..0f75fd44ef4a0 100644 --- a/src/platform/plugins/shared/telemetry/tsconfig.json +++ b/src/platform/plugins/shared/telemetry/tsconfig.json @@ -34,12 +34,12 @@ "@kbn/analytics-collection-utils", "@kbn/react-kibana-mount", "@kbn/core-node-server", - "@kbn/security-plugin-types-server", "@kbn/core-user-profile-browser-mocks", "@kbn/core-analytics-browser", "@kbn/core-analytics-server", "@kbn/core-elasticsearch-server", "@kbn/logging", + "@kbn/security-plugin-types-common", ], "exclude": [ "target/**/*", diff --git a/src/platform/plugins/shared/unified_histogram/public/__mocks__/suggestions.ts b/src/platform/plugins/shared/unified_histogram/public/__mocks__/suggestions.ts index 3d9a981b481ac..08e664e327047 100644 --- a/src/platform/plugins/shared/unified_histogram/public/__mocks__/suggestions.ts +++ b/src/platform/plugins/shared/unified_histogram/public/__mocks__/suggestions.ts @@ -185,12 +185,12 @@ export const histogramESQLSuggestionMock = { '662552df-2cdc-4539-bf3b-73b9f827252c': { index: 'e3465e67bdeced2befff9f9dca7ecf9c48504cad68a10efd881f4c7dd5ade28a', query: { - esql: 'from kibana_sample_data_logs | limit 10 | EVAL timestamp=DATE_TRUNC(30 second, @timestamp) | stats results = count(*) by timestamp | rename timestamp as `@timestamp every 30 second`', + esql: 'from kibana_sample_data_logs | limit 10 | EVAL timestamp=DATE_TRUNC(30 second, @timestamp) | stats results = count(*) by timestamp', }, columns: [ { - columnId: '@timestamp every 30 second', - fieldName: '@timestamp every 30 second', + columnId: 'timestamp', + fieldName: 'timestamp', meta: { type: 'date', }, diff --git a/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.attributes.test.ts b/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.attributes.test.ts index f338ef955c01e..b4336aee636ba 100644 --- a/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.attributes.test.ts +++ b/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.attributes.test.ts @@ -678,7 +678,7 @@ describe('LensVisService attributes', () => { ], "query": Object { "esql": "from logstash-* | limit 10 - | EVAL timestamp=DATE_TRUNC(10 minute, timestamp) | stats results = count(*) by timestamp | rename timestamp as \`timestamp every 10 minute\`", + | EVAL timestamp=DATE_TRUNC(10 minute, timestamp) | stats results = count(*) by timestamp", }, "visualization": Object { "gridConfig": Object { @@ -757,7 +757,7 @@ describe('LensVisService attributes', () => { it('should use the correct histogram query when no suggestion passed', async () => { const histogramQuery = { esql: `from logstash-* | limit 10 -| EVAL timestamp=DATE_TRUNC(10 minute, @timestamp) | stats results = count(*) by timestamp | rename timestamp as \`@timestamp every 10 minute\``, +| EVAL timestamp=DATE_TRUNC(10 minute, @timestamp) | stats results = count(*) by timestamp`, }; const lensVis = await getLensVisMock({ filters, diff --git a/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.suggestions.test.ts b/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.suggestions.test.ts index baeb330180ab8..a514d7f9fce51 100644 --- a/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.suggestions.test.ts +++ b/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.suggestions.test.ts @@ -125,7 +125,7 @@ describe('LensVisService suggestions', () => { const histogramQuery = { esql: `from the-data-view | limit 100 -| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp | rename timestamp as \`@timestamp every 30 minute\``, +| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp`, }; expect(lensVis.visContext?.attributes.state.query).toStrictEqual(histogramQuery); @@ -163,7 +163,7 @@ describe('LensVisService suggestions', () => { const histogramQuery = { esql: `from the-data-view | limit 100 -| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp | rename timestamp as \`@timestamp every 30 minute\``, +| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp`, }; expect(lensVis.visContext?.attributes.state.query).toStrictEqual(histogramQuery); @@ -248,7 +248,7 @@ describe('LensVisService suggestions', () => { const histogramQuery = { esql: `from the-data-view | limit 100 -| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp, \`var0\` | sort \`var0\` asc | rename timestamp as \`@timestamp every 30 minute\``, +| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp, \`var0\` | sort \`var0\` asc`, }; expect(lensVis.visContext?.attributes.state.query).toStrictEqual(histogramQuery); @@ -329,7 +329,7 @@ describe('LensVisService suggestions', () => { const histogramQuery = { esql: `from the-data-view | limit 100 -| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp, \`coordinates\` | rename timestamp as \`@timestamp every 30 minute\``, +| EVAL timestamp=DATE_TRUNC(30 minute, @timestamp) | stats results = count(*) by timestamp, \`coordinates\``, }; expect(lensVis.visContext?.attributes.state.query).toStrictEqual(histogramQuery); diff --git a/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.ts b/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.ts index 52968693ed650..cd3c1f761d26e 100644 --- a/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.ts +++ b/src/platform/plugins/shared/unified_histogram/public/services/lens_vis_service.ts @@ -49,6 +49,7 @@ import { deriveLensSuggestionFromLensAttributes, type QueryParams, injectESQLQueryIntoLensLayers, + TIMESTAMP_COLUMN, } from '../utils/external_vis_context'; import { computeInterval } from '../utils/compute_interval'; import { enrichLensAttributesWithTablesData } from '../utils/lens_vis_from_table'; @@ -496,13 +497,14 @@ export class LensVisService { interval, breakdownColumn, }); + const dateFieldLabel = `${dataView.timeFieldName} every ${interval}`; const context = { dataViewSpec: dataView?.toSpec(), fieldName: '', textBasedColumns: [ { - id: `${dataView.timeFieldName} every ${interval}`, - name: `${dataView.timeFieldName} every ${interval}`, + id: TIMESTAMP_COLUMN, + name: dateFieldLabel, meta: { type: 'date', }, @@ -526,9 +528,13 @@ export class LensVisService { // here the attributes contain the main query and not the histogram one const updatedAttributesWithQuery = preferredVisAttributes - ? injectESQLQueryIntoLensLayers(preferredVisAttributes, { - esql: esqlQuery, - }) + ? injectESQLQueryIntoLensLayers( + preferredVisAttributes, + { + esql: esqlQuery, + }, + dateFieldLabel + ) : undefined; const suggestions = @@ -598,7 +604,7 @@ export class LensVisService { return appendToESQLQuery( safeQuery, - `| EVAL timestamp=DATE_TRUNC(${queryInterval}, ${dataView.timeFieldName}) | stats results = count(*) by timestamp${breakdown}${sortBy} | rename timestamp as \`${dataView.timeFieldName} every ${queryInterval}\`` + `| EVAL ${TIMESTAMP_COLUMN}=DATE_TRUNC(${queryInterval}, ${dataView.timeFieldName}) | stats results = count(*) by ${TIMESTAMP_COLUMN}${breakdown}${sortBy}` ); }; diff --git a/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.test.ts b/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.test.ts index 1cbad8b308078..95abb9fcd4871 100644 --- a/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.test.ts +++ b/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.test.ts @@ -221,5 +221,72 @@ describe('external_vis_context', () => { injectESQLQueryIntoLensLayers(attributes, { esql: 'from foo | stats count(*)' }) ).toStrictEqual(expectedAttributes); }); + + it('should inject the interval to the Lens attributes for ES|QL config (textbased)', async () => { + const attributes = { + visualizationType: 'lnsXY', + state: { + visualization: { preferredSeriesType: 'line' }, + datasourceStates: { + textBased: { + layers: { + layer1: { + query: { esql: 'from foo' }, + columns: [ + { + columnId: 'col1', + fieldName: 'field1', + }, + { + columnId: 'timestamp', + fieldName: 'timestamp', + label: 'timestamp every 1h', + customLabel: true, + }, + ], + }, + }, + }, + }, + }, + } as unknown as UnifiedHistogramVisContext['attributes']; + + const expectedAttributes = { + ...attributes, + state: { + ...attributes.state, + datasourceStates: { + ...attributes.state.datasourceStates, + textBased: { + ...attributes.state.datasourceStates.textBased, + layers: { + layer1: { + query: { esql: 'from foo' }, + columns: [ + { + columnId: 'col1', + fieldName: 'field1', + }, + { + columnId: 'timestamp', + fieldName: 'timestamp', + label: 'timestamp every 10 minutes', + customLabel: true, + }, + ], + }, + }, + }, + }, + }, + } as unknown as UnifiedHistogramVisContext['attributes']; + expect( + injectESQLQueryIntoLensLayers( + attributes, + { esql: 'from foo' }, + 'timestamp every 10 minutes' + ) + ).toStrictEqual(expectedAttributes); + }); }); }); diff --git a/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.ts b/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.ts index 29e393d145087..cf55549691178 100644 --- a/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.ts +++ b/src/platform/plugins/shared/unified_histogram/public/utils/external_vis_context.ts @@ -10,12 +10,15 @@ import { isEqual, cloneDeep } from 'lodash'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; +import type { TextBasedLayerColumn } from '@kbn/lens-plugin/public/datasources/text_based/types'; import { getDatasourceId } from '@kbn/visualization-utils'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import type { PieVisualizationState, Suggestion, XYState } from '@kbn/lens-plugin/public'; import { UnifiedHistogramSuggestionType, UnifiedHistogramVisContext } from '../types'; import { removeTablesFromLensAttributes } from './lens_vis_from_table'; +export const TIMESTAMP_COLUMN = 'timestamp'; + export interface QueryParams { dataView: DataView; query?: Query | AggregateQuery; @@ -104,9 +107,21 @@ export const isSuggestionShapeAndVisContextCompatible = ( ); }; +const injectIntervalToDateTimeColumn = ( + columns: TextBasedLayerColumn[], + dateFieldLabel: string +) => { + const dateColumn = columns.find((column) => column.columnId === TIMESTAMP_COLUMN); + if (dateColumn && dateColumn.label !== dateFieldLabel && dateColumn.customLabel) { + dateColumn.label = dateFieldLabel; + } + return columns; +}; + export const injectESQLQueryIntoLensLayers = ( visAttributes: UnifiedHistogramVisContext['attributes'], - query: AggregateQuery + query: AggregateQuery, + dateFieldLabel?: string ) => { const datasourceId = getDatasourceId(visAttributes.state.datasourceStates); @@ -126,6 +141,12 @@ export const injectESQLQueryIntoLensLayers = ( if (!isEqual(layer.query, query)) { layer.query = query; } + if (dateFieldLabel && layer.columns) { + const columns = injectIntervalToDateTimeColumn(layer.columns, dateFieldLabel); + if (!isEqual(layer.columns, columns)) { + layer.columns = columns; + } + } }); } return { diff --git a/test/functional/apps/console/_autocomplete.ts b/test/functional/apps/console/_autocomplete.ts index 828be2fb84274..2f771a2f70097 100644 --- a/test/functional/apps/console/_autocomplete.ts +++ b/test/functional/apps/console/_autocomplete.ts @@ -159,15 +159,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.console.pressEnter(); await PageObjects.console.sleepForDebouncePeriod(); - expect((await PageObjects.console.getEditorText()).replace(/\s/g, '')).to.be.eql( + // Verify that the autocomplete suggestion is inserted into the editor + expect((await PageObjects.console.getEditorText()).replace(/\s/g, '')).to.contain( ` -GET _search -{ - "aggs": { - "NAME": { - "AGG_TYPE": {} - } - } +"aggs": { + "NAME": { + "AGG_TYPE": {} + } } `.replace(/\s/g, '') ); diff --git a/test/functional/apps/management/data_views/_handle_version_conflict.ts b/test/functional/apps/management/data_views/_handle_version_conflict.ts index b95667c2a7ab0..8853d43d06c69 100644 --- a/test/functional/apps/management/data_views/_handle_version_conflict.ts +++ b/test/functional/apps/management/data_views/_handle_version_conflict.ts @@ -21,7 +21,6 @@ import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); const kibanaServer = getService('kibanaServer'); const browser = getService('browser'); const es = getService('es'); @@ -31,8 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const toasts = getService('toasts'); - // Failing: See https://github.com/elastic/kibana/issues/193102 - describe.skip('FOO index version conflict', function describeIndexTests() { + describe('FOO index version conflict', function describeIndexTests() { before(async function () { await browser.setWindowSize(1200, 800); await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); @@ -77,11 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug('Starting openControlsByName (' + fieldName + ')'); await PageObjects.settings.openControlsByName(fieldName); log.debug('controls are open'); - await ( - await ( - await testSubjects.find('formatRow') - ).findAllByCssSelector('[data-test-subj="toggle"]') - )[0].click(); + await PageObjects.settings.toggleRow('formatRow'); await PageObjects.settings.setFieldFormat('url'); const response = await es.update( { diff --git a/test/functional/apps/visualize/group6/_vega_chart.ts b/test/functional/apps/visualize/group6/_vega_chart.ts index e833bf233ca9f..1bfc7ab267b68 100644 --- a/test/functional/apps/visualize/group6/_vega_chart.ts +++ b/test/functional/apps/visualize/group6/_vega_chart.ts @@ -53,6 +53,16 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); describe('vega chart', () => { + it('code-editor correct syntax highlight langs', async () => { + const hasRequiredLanguages = await browser.execute(() => { + const langs: Array<{ id: string }> = + // @ts-ignore + window.MonacoEnvironment?.monaco?.languages?.getLanguages() ?? []; + return langs.some((l) => l?.id === 'hjson') && langs.some((l) => l?.id === 'xjson'); + }); + expect(hasRequiredLanguages).to.be(true); + }); + describe('initial render', () => { it('should have some initial vega spec text', async function () { const vegaSpec = await vegaChart.getSpec(); diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index dcb38a8cf3e7c..c08eb75bcf1d1 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -407,6 +407,9 @@ export class SettingsPageObject extends FtrService { `table.euiTable tbody tr.euiTableRow:nth-child(${tableFields.indexOf(name) + 1}) td:nth-last-child(2) button` ); + await this.retry.waitFor('flyout to open', async () => { + return await this.testSubjects.exists('flyoutTitle'); + }); } async increasePopularity() { diff --git a/tsconfig.base.json b/tsconfig.base.json index e4bb31ffd9038..f5ef65eab03bd 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1078,6 +1078,8 @@ "@kbn/inference-endpoint-plugin/*": ["x-pack/platform/plugins/shared/inference_endpoint/*"], "@kbn/inference-endpoint-ui-common": ["x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common"], "@kbn/inference-endpoint-ui-common/*": ["x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/*"], + "@kbn/inference-langchain": ["x-pack/platform/packages/shared/ai-infra/inference-langchain"], + "@kbn/inference-langchain/*": ["x-pack/platform/packages/shared/ai-infra/inference-langchain/*"], "@kbn/inference-plugin": ["x-pack/platform/plugins/shared/inference"], "@kbn/inference-plugin/*": ["x-pack/platform/plugins/shared/inference/*"], "@kbn/infra-forge": ["x-pack/platform/packages/private/kbn-infra-forge"], @@ -1724,8 +1726,6 @@ "@kbn/serverless-observability/*": ["x-pack/solutions/observability/plugins/serverless_observability/*"], "@kbn/serverless-observability-settings": ["src/platform/packages/shared/serverless/settings/observability_project"], "@kbn/serverless-observability-settings/*": ["src/platform/packages/shared/serverless/settings/observability_project/*"], - "@kbn/serverless-project-switcher": ["src/platform/packages/private/serverless/project_switcher"], - "@kbn/serverless-project-switcher/*": ["src/platform/packages/private/serverless/project_switcher/*"], "@kbn/serverless-search": ["x-pack/solutions/search/plugins/serverless_search"], "@kbn/serverless-search/*": ["x-pack/solutions/search/plugins/serverless_search/*"], "@kbn/serverless-search-settings": ["src/platform/packages/shared/serverless/settings/search_project"], diff --git a/x-pack/platform/packages/private/security/authorization_core/src/actions/api.ts b/x-pack/platform/packages/private/security/authorization_core/src/actions/api.ts index d91bc1bd89669..7bd8b019af088 100644 --- a/x-pack/platform/packages/private/security/authorization_core/src/actions/api.ts +++ b/x-pack/platform/packages/private/security/authorization_core/src/actions/api.ts @@ -7,8 +7,8 @@ import { isString } from 'lodash'; +import { ApiOperation } from '@kbn/security-plugin-types-common'; import type { ApiActions as ApiActionsType } from '@kbn/security-plugin-types-server'; -import { ApiOperation } from '@kbn/security-plugin-types-server'; export class ApiActions implements ApiActionsType { private readonly prefix: string; diff --git a/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.test.ts b/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.test.ts index 04770495fb2cf..cbd6032761f1b 100644 --- a/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.test.ts +++ b/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.test.ts @@ -7,7 +7,7 @@ import { KibanaFeature } from '@kbn/features-plugin/server'; import { featuresPluginMock } from '@kbn/features-plugin/server/mocks'; -import { ApiOperation } from '@kbn/security-plugin-types-server'; +import { ApiOperation } from '@kbn/security-plugin-types-common'; import { getReplacedByForPrivilege, privilegesFactory } from './privileges'; import { licenseMock } from '../__fixtures__/licensing.mock'; diff --git a/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.ts b/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.ts index f266b2b9a7085..433f26785da84 100644 --- a/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.ts +++ b/x-pack/platform/packages/private/security/authorization_core/src/privileges/privileges.ts @@ -17,7 +17,7 @@ import { isMinimalPrivilegeId, } from '@kbn/security-authorization-core-common'; import type { RawKibanaPrivileges, SecurityLicense } from '@kbn/security-plugin-types-common'; -import { ApiOperation } from '@kbn/security-plugin-types-server'; +import { ApiOperation } from '@kbn/security-plugin-types-common'; import { featurePrivilegeBuilderFactory } from './feature_privilege_builder'; import type { Actions } from '../actions'; diff --git a/x-pack/platform/packages/private/security/authorization_core_common/index.ts b/x-pack/platform/packages/private/security/authorization_core_common/index.ts index 63fa40559fae2..3963bd5fe2577 100644 --- a/x-pack/platform/packages/private/security/authorization_core_common/index.ts +++ b/x-pack/platform/packages/private/security/authorization_core_common/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { isMinimalPrivilegeId, getMinimalPrivilegeId } from './src/privileges'; +export { isMinimalPrivilegeId, getMinimalPrivilegeId, ApiPrivileges } from './src/privileges'; diff --git a/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/api_privileges.ts b/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/api_privileges.ts new file mode 100644 index 0000000000000..7eb8001b9fab0 --- /dev/null +++ b/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/api_privileges.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ApiOperation } from '@kbn/security-plugin-types-common'; + +export class ApiPrivileges { + public static manage(subject: string) { + return `${ApiOperation.Manage}_${subject}`; + } + + public static read(subject: string) { + return `${ApiOperation.Read}_${subject}`; + } + + public static create(subject: string) { + return `${ApiOperation.Create}_${subject}`; + } + + public static update(subject: string) { + return `${ApiOperation.Update}_${subject}`; + } + + public static delete(subject: string) { + return `${ApiOperation.Delete}_${subject}`; + } +} diff --git a/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/index.ts b/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/index.ts index 01e05bfabde5c..3b678644f63be 100644 --- a/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/index.ts +++ b/x-pack/platform/packages/private/security/authorization_core_common/src/privileges/index.ts @@ -6,3 +6,4 @@ */ export { isMinimalPrivilegeId, getMinimalPrivilegeId } from './minimal_privileges'; +export { ApiPrivileges } from './api_privileges'; diff --git a/x-pack/platform/packages/private/security/authorization_core_common/tsconfig.json b/x-pack/platform/packages/private/security/authorization_core_common/tsconfig.json index 597c1b47cb03a..b7a95d84501e8 100644 --- a/x-pack/platform/packages/private/security/authorization_core_common/tsconfig.json +++ b/x-pack/platform/packages/private/security/authorization_core_common/tsconfig.json @@ -6,5 +6,7 @@ }, "include": ["**/*.ts", "**/*.tsx"], "exclude": ["target/**/*"], - "kbn_references": [] + "kbn_references": [ + "@kbn/security-plugin-types-common", + ] } diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/index.ts index b213958ae444a..f1835ab1ab52c 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/index.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/index.ts @@ -96,6 +96,7 @@ export { isInferenceRequestError, isInferenceRequestAbortedError, } from './src/errors'; +export { generateFakeToolCallId } from './src/utils'; export { elasticModelDictionary } from './src/const'; export { truncateList } from './src/truncate_list'; @@ -103,6 +104,9 @@ export { InferenceConnectorType, isSupportedConnectorType, isSupportedConnector, + getConnectorDefaultModel, + getConnectorProvider, + connectorToInference, type InferenceConnector, } from './src/connectors'; export { diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/messages.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/messages.ts index 54b9b76d2bd8c..7fdd78670fa1b 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/messages.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/chat_complete/messages.ts @@ -69,7 +69,7 @@ export type ToolMessage< TToolResponse extends Record | unknown = Record | unknown, TToolData extends Record | undefined = Record | undefined > = MessageBase & { - /* + /** * The name of the tool called. Used for refining the type of the response. */ name: TName; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connector_config.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connector_config.ts new file mode 100644 index 0000000000000..e2fc26cc9a7e6 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connector_config.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { type InferenceConnector, InferenceConnectorType } from './connectors'; + +/** + * Returns the default model as defined in the connector's config, if available. + * + * Note: preconfigured connectors only expose their config if their `exposeConfig` flag + * is set to true. + */ +export const getConnectorDefaultModel = (connector: InferenceConnector): string | undefined => { + switch (connector.type) { + case InferenceConnectorType.OpenAI: + case InferenceConnectorType.Gemini: + case InferenceConnectorType.Bedrock: + return connector.config?.defaultModel ?? undefined; + case InferenceConnectorType.Inference: + return connector.config?.providerConfig?.model_id ?? undefined; + } +}; + +/** + * Returns the provider used for the given connector + * + * Inferred from the type for "legacy" connectors, + * and from the provider config field for inference connectors. + */ +export const getConnectorProvider = (connector: InferenceConnector): string => { + switch (connector.type) { + case InferenceConnectorType.OpenAI: + return 'openai'; + case InferenceConnectorType.Gemini: + return 'gemini'; + case InferenceConnectorType.Bedrock: + return 'bedrock'; + case InferenceConnectorType.Inference: + return connector.config?.provider ?? 'unknown'; + } +}; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connector_to_inference.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connector_to_inference.ts new file mode 100644 index 0000000000000..bb74b40b53ec5 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connector_to_inference.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createInferenceRequestError } from '../errors'; +import type { InferenceConnector, RawConnector } from './connectors'; +import { isSupportedConnector } from './is_supported_connector'; + +/** + * Converts an action connector to the internal inference connector format. + * + * The function will throw if the provided connector is not compatible + */ +export const connectorToInference = (connector: RawConnector): InferenceConnector => { + if (!isSupportedConnector(connector)) { + throw createInferenceRequestError( + `Connector '${connector.id}' of type '${connector.actionTypeId}' not recognized as a supported connector`, + 400 + ); + } + + return { + connectorId: connector.id, + name: connector.name, + type: connector.actionTypeId, + config: connector.config ?? {}, + }; +}; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connectors.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connectors.ts new file mode 100644 index 0000000000000..2ef0711e1e4cb --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/connectors.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * The list of connector types that can be used with the inference APIs + */ +export enum InferenceConnectorType { + OpenAI = '.gen-ai', + Bedrock = '.bedrock', + Gemini = '.gemini', + Inference = '.inference', +} + +export const allSupportedConnectorTypes = Object.values(InferenceConnectorType); + +/** + * Represents a stack connector that can be used for inference. + */ +export interface InferenceConnector { + /** the type of the connector, see {@link InferenceConnectorType} */ + type: InferenceConnectorType; + /** the name of the connector */ + name: string; + /** the id of the connector */ + connectorId: string; + /** + * configuration (without secrets) of the connector. + * the list of properties depends on the connector type (and subtype for inference) + */ + config: Record; +} + +/** + * Connector types are living in the actions plugin and we can't afford + * having dependencies from this package to some mid-level plugin, + * so we're just using our own connector mixin type. + */ +export interface RawConnector { + id: string; + actionTypeId: string; + name: string; + config?: Record; +} + +export interface RawInferenceConnector { + id: string; + actionTypeId: InferenceConnectorType; + name: string; + config?: Record; +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/index.ts new file mode 100644 index 0000000000000..33e28c8cf2e7d --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { isSupportedConnectorType, isSupportedConnector } from './is_supported_connector'; +export { connectorToInference } from './connector_to_inference'; +export { getConnectorDefaultModel, getConnectorProvider } from './connector_config'; +export { InferenceConnectorType, type InferenceConnector } from './connectors'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors.test.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/is_supported_connector.test.ts similarity index 96% rename from x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors.test.ts rename to x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/is_supported_connector.test.ts index a4729aa8a8578..1daa3a02a8711 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors.test.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/is_supported_connector.test.ts @@ -5,13 +5,12 @@ * 2.0. */ +import { InferenceConnectorType, RawConnector } from './connectors'; import { - InferenceConnectorType, isSupportedConnectorType, isSupportedConnector, - RawConnector, COMPLETION_TASK_TYPE, -} from './connectors'; +} from './is_supported_connector'; const createRawConnector = (parts: Partial): RawConnector => { return { @@ -36,8 +35,6 @@ describe('isSupportedConnectorType', () => { }); describe('isSupportedConnector', () => { - // TODO - it('returns true for OpenAI connectors', () => { expect( isSupportedConnector(createRawConnector({ actionTypeId: InferenceConnectorType.OpenAI })) diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/is_supported_connector.ts similarity index 53% rename from x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors.ts rename to x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/is_supported_connector.ts index b80996864d68d..c397a073aa1b9 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/connectors/is_supported_connector.ts @@ -5,37 +5,15 @@ * 2.0. */ -/** - * The list of connector types that can be used with the inference APIs - */ -export enum InferenceConnectorType { - OpenAI = '.gen-ai', - Bedrock = '.bedrock', - Gemini = '.gemini', - Inference = '.inference', -} +import { + InferenceConnectorType, + RawInferenceConnector, + RawConnector, + allSupportedConnectorTypes, +} from './connectors'; export const COMPLETION_TASK_TYPE = 'chat_completion'; -const allSupportedConnectorTypes = Object.values(InferenceConnectorType); - -/** - * Represents a stack connector that can be used for inference. - */ -export interface InferenceConnector { - /** the type of the connector, see {@link InferenceConnectorType} */ - type: InferenceConnectorType; - /** the name of the connector */ - name: string; - /** the id of the connector */ - connectorId: string; - /** - * configuration (without secrets) of the connector. - * the list of properties depends on the connector type (and subtype for inference) - */ - config: Record; -} - /** * Checks if a given connector type is compatible for inference. * @@ -67,22 +45,3 @@ export function isSupportedConnector(connector: RawConnector): connector is RawI } return true; } - -/** - * Connector types are living in the actions plugin and we can't afford - * having dependencies from this package to some mid-level plugin, - * so we're just using our own connector mixin type. - */ -export interface RawConnector { - id: string; - actionTypeId: string; - name: string; - config?: Record; -} - -interface RawInferenceConnector { - id: string; - actionTypeId: InferenceConnectorType; - name: string; - config?: Record; -} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/errors.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/errors.ts index 472ed50e231f5..faf0e892e2e83 100644 --- a/x-pack/platform/packages/shared/ai-infra/inference-common/src/errors.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/errors.ts @@ -27,6 +27,13 @@ export class InferenceTaskError< super(message); } + public get status() { + if (typeof this.meta === 'object' && this.meta.status) { + return this.meta.status as number; + } + return undefined; + } + toJSON(): InferenceTaskErrorEvent { return { type: InferenceTaskEventType.error, diff --git a/x-pack/platform/packages/shared/ai-infra/inference-common/src/utils/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/utils/index.ts new file mode 100644 index 0000000000000..63d85d205ab19 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/utils/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { generateFakeToolCallId } from './tool_calls'; diff --git a/x-pack/test/functional_enterprise_search/ftr_provider_context.d.ts b/x-pack/platform/packages/shared/ai-infra/inference-common/src/utils/tool_calls.ts similarity index 51% rename from x-pack/test/functional_enterprise_search/ftr_provider_context.d.ts rename to x-pack/platform/packages/shared/ai-infra/inference-common/src/utils/tool_calls.ts index 24f5087ef7fe2..1da675b61c4ec 100644 --- a/x-pack/test/functional_enterprise_search/ftr_provider_context.d.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-common/src/utils/tool_calls.ts @@ -5,9 +5,8 @@ * 2.0. */ -import { GenericFtrProviderContext } from '@kbn/test'; +import { v4 } from 'uuid'; -import { pageObjects } from './page_objects'; -import { services } from './services'; - -export type FtrProviderContext = GenericFtrProviderContext; +export function generateFakeToolCallId() { + return v4().substr(0, 6); +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/README.md b/x-pack/platform/packages/shared/ai-infra/inference-langchain/README.md new file mode 100644 index 0000000000000..dc44528b659b0 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/README.md @@ -0,0 +1,39 @@ +# @kbn/inference-langchain + +This package exposes utilities to use the inference APIs and plugin with langchain + +## InferenceChatModel + +The inference chat model is a langchain model leveraging the inference APIs under the hood. + +The main upside is that the unification and normalization layers are then fully handled +by the inference plugin. The developer / consumer doesn't even need to know which provider +is being used under the hood. + +The easiest way to create an `InferenceChatModel` is by using the inference APIs: + +```ts +const chatModel = await inferenceStart.getChatModel({ + request, + connectorId: myInferenceConnectorId, + chatModelOptions: { + temperature: 0.2, + }, +}); + +// just use it as another langchain chatModel +``` + +But the chatModel can also be instantiated directly if needed: + +```ts +import { connectorToInference } from '@kbn/inference-common'; + +const chatModel = new InferenceChatModel({ + chatComplete: inference.chatComplete, + connector: connectorToInference(someInferenceConnector), + logger: myPluginLogger, +}); + +// just use it as another langchain chatModel +``` diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/index.ts new file mode 100644 index 0000000000000..700cc0083625a --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { + InferenceChatModel, + type InferenceChatModelParams, + type InferenceChatModelCallOptions, +} from './src/chat_model'; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/types.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/jest.config.js similarity index 53% rename from x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/types.ts rename to x-pack/platform/packages/shared/ai-infra/inference-langchain/jest.config.js index f999ebb5e5db0..de5dbddb01552 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/types.ts +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/jest.config.js @@ -5,15 +5,8 @@ * 2.0. */ -import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; - -export type VirtualCard = { - type: 'virtual'; -} & IntegrationCardItem; - -export interface FeaturedCard { - type: 'featured'; - name: string; -} - -export type CustomCard = FeaturedCard | VirtualCard; +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../../..', + roots: ['/x-pack/platform/packages/shared/ai-infra/inference-langchain'], +}; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/kibana.jsonc b/x-pack/platform/packages/shared/ai-infra/inference-langchain/kibana.jsonc new file mode 100644 index 0000000000000..b39cb28bb4595 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/inference-langchain", + "owner": "@elastic/appex-ai-infra" +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/package.json b/x-pack/platform/packages/shared/ai-infra/inference-langchain/package.json new file mode 100644 index 0000000000000..52a59194fab83 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/inference-langchain", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts new file mode 100644 index 0000000000000..a64e59c99f876 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/chunks.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChatCompletionChunkEvent, ChatCompletionTokenCountEvent } from '@kbn/inference-common'; +import { AIMessageChunk } from '@langchain/core/messages'; + +// type is not exported from @langchain/core... +// import { ToolCallChunk } from '@langchain/core/messages/tools'; +type ToolCallChunk = Required['tool_call_chunks'][number]; + +export const completionChunkToLangchain = (chunk: ChatCompletionChunkEvent): AIMessageChunk => { + const toolCallChunks = chunk.tool_calls.map((toolCall) => { + return { + index: toolCall.index, + id: toolCall.toolCallId, + name: toolCall.function.name, + args: toolCall.function.arguments, + type: 'tool_call_chunk', + }; + }); + + return new AIMessageChunk({ + content: chunk.content, + tool_call_chunks: toolCallChunks, + additional_kwargs: {}, + response_metadata: {}, + }); +}; + +export const tokenCountChunkToLangchain = ( + chunk: ChatCompletionTokenCountEvent +): AIMessageChunk => { + return new AIMessageChunk({ + content: '', + response_metadata: { + usage: { ...chunk.tokens }, + }, + usage_metadata: { + input_tokens: chunk.tokens.prompt, + output_tokens: chunk.tokens.completion, + total_tokens: chunk.tokens.total, + }, + }); +}; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/index.ts new file mode 100644 index 0000000000000..7f94903b5c41a --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { completionChunkToLangchain, tokenCountChunkToLangchain } from './chunks'; +export { responseToLangchainMessage } from './messages'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/messages.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/messages.ts new file mode 100644 index 0000000000000..6e4388e6d9b4a --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/from_inference/messages.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ChatCompleteResponse } from '@kbn/inference-common'; +import { AIMessage } from '@langchain/core/messages'; + +export const responseToLangchainMessage = (response: ChatCompleteResponse): AIMessage => { + return new AIMessage({ + content: response.content, + tool_calls: response.toolCalls.map((toolCall) => { + return { + id: toolCall.toolCallId, + name: toolCall.function.name, + args: toolCall.function.arguments, + type: 'tool_call', + }; + }), + }); +}; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/index.ts new file mode 100644 index 0000000000000..fdd11796e35ff --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { + InferenceChatModel, + type InferenceChatModelParams, + type InferenceChatModelCallOptions, +} from './inference_chat_model'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts new file mode 100644 index 0000000000000..2e52dc3a2f1f1 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.test.ts @@ -0,0 +1,927 @@ +/* + * Copyright 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 { of, Observable } from 'rxjs'; +import { z } from '@kbn/zod'; +import { + AIMessage, + AIMessageChunk, + HumanMessage, + isAIMessage, + SystemMessage, + ToolMessage, +} from '@langchain/core/messages'; +import { loggerMock, MockedLogger } from '@kbn/logging-mocks'; +import { + ChatCompleteAPI, + ChatCompleteResponse, + ChatCompleteStreamResponse, + ChatCompletionChunkEvent, + ChatCompletionEvent, + ChatCompletionEventType, + ChatCompletionTokenCount, + InferenceConnector, + InferenceConnectorType, + MessageRole, + createInferenceRequestError, +} from '@kbn/inference-common'; +import { InferenceChatModel } from './inference_chat_model'; + +const createConnector = (parts: Partial = {}): InferenceConnector => { + return { + type: InferenceConnectorType.Inference, + connectorId: 'connector-id', + name: 'My connector', + config: {}, + ...parts, + }; +}; + +const createResponse = (parts: Partial = {}): ChatCompleteResponse => { + return { + content: 'content', + toolCalls: [], + tokens: undefined, + ...parts, + }; +}; + +const createStreamResponse = ( + chunks: ChunkEventInput[], + tokenCount?: ChatCompletionTokenCount +): ChatCompleteStreamResponse => { + const events: ChatCompletionEvent[] = chunks.map(createChunkEvent); + if (tokenCount) { + events.push({ + type: ChatCompletionEventType.ChatCompletionTokenCount, + tokens: tokenCount, + }); + } + const finalContent = chunks + .map((chunk) => { + return typeof chunk === 'string' ? chunk : chunk.content; + }) + .join(''); + events.push({ + type: ChatCompletionEventType.ChatCompletionMessage, + content: finalContent, + toolCalls: [], // final message isn't used anyway so no need to compute this + }); + + return of(...events); +}; + +type ChunkEventInput = string | Partial>; + +const createChunkEvent = (input: ChunkEventInput): ChatCompletionChunkEvent => { + if (typeof input === 'string') { + return { + type: ChatCompletionEventType.ChatCompletionChunk, + content: input, + tool_calls: [], + }; + } else { + return { + type: ChatCompletionEventType.ChatCompletionChunk, + content: '', + tool_calls: [], + ...input, + }; + } +}; + +describe('InferenceChatModel', () => { + let chatComplete: ChatCompleteAPI & jest.MockedFn; + let connector: InferenceConnector; + let logger: MockedLogger; + + beforeEach(() => { + logger = loggerMock.create(); + chatComplete = jest.fn(); + connector = createConnector(); + }); + + describe('Request conversion', () => { + it('converts a basic message call', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + await chatModel.invoke('Some question'); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [ + { + role: MessageRole.User, + content: 'Some question', + }, + ], + stream: false, + }); + }); + + it('converts a complete conversation call', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + await chatModel.invoke([ + new SystemMessage({ + content: 'system instructions', + }), + new HumanMessage({ + content: 'question', + }), + new AIMessage({ + content: 'answer', + }), + new HumanMessage({ + content: 'another question', + }), + ]); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + system: 'system instructions', + messages: [ + { + role: MessageRole.User, + content: 'question', + }, + { + role: MessageRole.Assistant, + content: 'answer', + }, + { + role: MessageRole.User, + content: 'another question', + }, + ], + stream: false, + }); + }); + + it('converts a tool call conversation', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + await chatModel.invoke([ + new HumanMessage({ + content: 'question', + }), + new AIMessage({ + content: '', + tool_calls: [ + { + id: 'toolCallId', + name: 'myFunctionName', + args: { arg1: 'value1' }, + }, + ], + }), + new ToolMessage({ + tool_call_id: 'toolCallId', + content: '{ "response": 42 }', + }), + new AIMessage({ + content: 'answer', + }), + new HumanMessage({ + content: 'another question', + }), + ]); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [ + { + content: 'question', + role: 'user', + }, + { + role: 'assistant', + content: '', + toolCalls: [ + { + toolCallId: 'toolCallId', + function: { + arguments: { + arg1: 'value1', + }, + name: 'myFunctionName', + }, + }, + ], + }, + { + role: 'tool', + name: 'toolCallId', + response: '{ "response": 42 }', + toolCallId: 'toolCallId', + }, + { + content: 'answer', + role: 'assistant', + }, + { + content: 'another question', + role: 'user', + }, + ], + stream: false, + }); + }); + + it('converts tools', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + await chatModel.invoke( + [ + new HumanMessage({ + content: 'question', + }), + ], + { + tools: [ + { + name: 'test_tool', + description: 'Just some test tool', + schema: z.object({ + city: z.string().describe('The city to get the weather for'), + zipCode: z.number().optional().describe('The zipCode to get the weather for'), + }), + }, + ], + } + ); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [ + { + role: MessageRole.User, + content: 'question', + }, + ], + tools: { + test_tool: { + description: 'Just some test tool', + schema: { + properties: { + city: { + description: 'The city to get the weather for', + type: 'string', + }, + zipCode: { + description: 'The zipCode to get the weather for', + type: 'number', + }, + }, + required: ['city'], + type: 'object', + }, + }, + }, + stream: false, + }); + }); + + it('uses constructor parameters', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + temperature: 0.7, + model: 'super-duper-model', + functionCallingMode: 'simulated', + }); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + await chatModel.invoke('question'); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [{ role: MessageRole.User, content: 'question' }], + functionCalling: 'simulated', + temperature: 0.7, + modelName: 'super-duper-model', + stream: false, + }); + }); + + it('uses invocation parameters', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + temperature: 0.7, + model: 'super-duper-model', + functionCallingMode: 'simulated', + }); + + const abortCtrl = new AbortController(); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + await chatModel.invoke('question', { + temperature: 0, + model: 'some-other-model', + signal: abortCtrl.signal, + tool_choice: 'auto', + }); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [{ role: MessageRole.User, content: 'question' }], + toolChoice: 'auto', + functionCalling: 'simulated', + temperature: 0, + modelName: 'some-other-model', + abortSignal: abortCtrl.signal, + stream: false, + }); + }); + }); + + describe('Response handling', () => { + it('returns the content', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ + content: 'response', + }); + chatComplete.mockResolvedValue(response); + + const output: AIMessage = await chatModel.invoke('Some question'); + + expect(isAIMessage(output)).toBe(true); + expect(output.content).toEqual('response'); + }); + + it('returns tool calls', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ + content: '', + toolCalls: [ + { + toolCallId: 'myToolCallId', + function: { + name: 'myToolName', + arguments: { + arg1: 'val1', + }, + }, + }, + ], + }); + chatComplete.mockResolvedValue(response); + + const output: AIMessage = await chatModel.invoke('Some question'); + + expect(output.content).toEqual(''); + expect(output.tool_calls).toEqual([ + { + id: 'myToolCallId', + name: 'myToolName', + args: { + arg1: 'val1', + }, + type: 'tool_call', + }, + ]); + }); + + it('returns the token count meta', async () => { + let rawOutput: Record; + + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + callbacks: [ + { + handleLLMEnd(_output) { + rawOutput = _output; + }, + }, + ], + }); + + const response = createResponse({ + content: 'response', + tokens: { + prompt: 5, + completion: 10, + total: 15, + }, + }); + chatComplete.mockResolvedValue(response); + + const output: AIMessage = await chatModel.invoke('Some question'); + + expect(output.response_metadata.tokenUsage).toEqual({ + promptTokens: 5, + completionTokens: 10, + totalTokens: 15, + }); + + expect(rawOutput!.llmOutput.tokenUsage).toEqual({ + promptTokens: 5, + completionTokens: 10, + totalTokens: 15, + }); + }); + + it('throws when the underlying call throws', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + maxRetries: 0, + }); + + chatComplete.mockImplementation(async () => { + throw new Error('something went wrong'); + }); + + await expect(() => + chatModel.invoke('Some question') + ).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong"`); + }); + + it('respects the maxRetries parameter', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + maxRetries: 1, + }); + + chatComplete + .mockImplementationOnce(async () => { + throw new Error('something went wrong'); + }) + .mockResolvedValueOnce( + createResponse({ + content: 'response', + }) + ); + + const output = await chatModel.invoke('Some question'); + + expect(output.content).toEqual('response'); + expect(chatComplete).toHaveBeenCalledTimes(2); + }); + + it('does not retry unrecoverable errors', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + maxRetries: 0, + }); + + chatComplete.mockImplementation(async () => { + throw createInferenceRequestError('bad parameter', 401); + }); + + await expect(() => + chatModel.invoke('Some question') + ).rejects.toThrowErrorMatchingInlineSnapshot(`"bad parameter"`); + + expect(chatComplete).toHaveBeenCalledTimes(1); + }); + }); + + describe('Streaming response handling', () => { + it('returns the chunks', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createStreamResponse(['hello ', 'there', '.']); + chatComplete.mockReturnValue(response); + + const output = await chatModel.stream('Some question'); + + const allChunks: AIMessageChunk[] = []; + for await (const chunk of output) { + allChunks.push(chunk); + } + + expect(allChunks.length).toBe(3); + expect(allChunks.map((chunk) => chunk.content)).toEqual(['hello ', 'there', '.']); + }); + + it('returns tool calls', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createStreamResponse([ + { + tool_calls: [ + { toolCallId: 'my-tool-call-id', index: 0, function: { name: '', arguments: '' } }, + ], + }, + { + tool_calls: [{ toolCallId: '', index: 0, function: { name: 'myfun', arguments: '' } }], + }, + { + tool_calls: [ + { toolCallId: '', index: 0, function: { name: 'ction', arguments: ' { "' } }, + ], + }, + { + tool_calls: [{ toolCallId: '', index: 0, function: { name: '', arguments: 'arg1": ' } }], + }, + { + tool_calls: [{ toolCallId: '', index: 0, function: { name: '', arguments: '42 }' } }], + }, + ]); + chatComplete.mockReturnValue(response); + + const output = await chatModel.stream('Some question'); + + const allChunks: AIMessageChunk[] = []; + let concatChunk: AIMessageChunk | undefined; + for await (const chunk of output) { + allChunks.push(chunk); + concatChunk = concatChunk ? concatChunk.concat(chunk) : chunk; + } + + expect(allChunks.length).toBe(5); + expect(concatChunk!.tool_calls).toEqual([ + { + id: 'my-tool-call-id', + name: 'myfunction', + args: { + arg1: 42, + }, + type: 'tool_call', + }, + ]); + }); + + it('returns the token count meta', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createStreamResponse(['hello ', 'there', '.'], { + prompt: 5, + completion: 20, + total: 25, + }); + chatComplete.mockReturnValue(response); + + const output = await chatModel.stream('Some question'); + + const allChunks: AIMessageChunk[] = []; + for await (const chunk of output) { + allChunks.push(chunk); + } + + expect(allChunks.length).toBe(4); + expect(allChunks.map((chunk) => chunk.content)).toEqual(['hello ', 'there', '.', '']); + expect(allChunks[3].usage_metadata).toEqual({ + input_tokens: 5, + output_tokens: 20, + total_tokens: 25, + }); + + const concatChunk = allChunks.reduce((concat, current) => { + return concat.concat(current); + }); + + expect(concatChunk.usage_metadata).toEqual({ + input_tokens: 5, + output_tokens: 20, + total_tokens: 25, + }); + }); + + it('throws when the underlying call throws', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + maxRetries: 0, + }); + + chatComplete.mockImplementation(async () => { + throw new Error('something went wrong'); + }); + + await expect(() => + chatModel.stream('Some question') + ).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong"`); + }); + + it('throws when the underlying observable errors', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = new Observable((subscriber) => { + subscriber.next(createChunkEvent('chunk1')); + subscriber.next(createChunkEvent('chunk2')); + subscriber.error(new Error('something went wrong')); + }); + chatComplete.mockReturnValue(response); + + const output = await chatModel.stream('Some question'); + + const allChunks: AIMessageChunk[] = []; + await expect(async () => { + for await (const chunk of output) { + allChunks.push(chunk); + } + }).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong"`); + + expect(allChunks.length).toBe(2); + }); + }); + + describe('#bindTools', () => { + it('bind tools to be used for invocation', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const response = createResponse({ content: 'dummy' }); + chatComplete.mockResolvedValue(response); + + const chatModelWithTools = chatModel.bindTools([ + { + name: 'test_tool', + description: 'Just some test tool', + schema: z.object({ + city: z.string().describe('The city to get the weather for'), + zipCode: z.number().optional().describe('The zipCode to get the weather for'), + }), + }, + ]); + + await chatModelWithTools.invoke([ + new HumanMessage({ + content: 'question', + }), + ]); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [ + { + role: MessageRole.User, + content: 'question', + }, + ], + tools: { + test_tool: { + description: 'Just some test tool', + schema: { + properties: { + city: { + description: 'The city to get the weather for', + type: 'string', + }, + zipCode: { + description: 'The zipCode to get the weather for', + type: 'number', + }, + }, + required: ['city'], + type: 'object', + }, + }, + }, + stream: false, + }); + }); + }); + + describe('#identifyingParams', () => { + it('returns connectorId and modelName from the constructor', () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + model: 'my-super-model', + }); + + const identifyingParams = chatModel.identifyingParams(); + + expect(identifyingParams).toEqual({ + connectorId: 'connector-id', + modelName: 'my-super-model', + model_name: 'my-super-model', + }); + }); + }); + + describe('#getLsParams', () => { + it('returns connectorId and modelName from the constructor', () => { + connector = createConnector({ + config: { + provider: 'elastic', + providerConfig: { + model_id: 'some-default-model-id', + }, + }, + }); + + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + model: 'my-super-model', + temperature: 0.7, + }); + + const lsParams = chatModel.getLsParams({}); + + expect(lsParams).toEqual({ + ls_model_name: 'my-super-model', + ls_model_type: 'chat', + ls_provider: 'inference-elastic', + ls_temperature: 0.7, + }); + }); + }); + + describe('#withStructuredOutput', () => { + it('binds the correct parameters', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const structuredOutputModel = chatModel.withStructuredOutput( + z + .object({ + city: z.string().describe('The city to get the weather for'), + zipCode: z.number().optional().describe('The zipCode to get the weather for'), + }) + .describe('Use to get the weather'), + { name: 'weather_tool' } + ); + + const response = createResponse({ + content: '', + toolCalls: [ + { + toolCallId: 'myToolCallId', + function: { + name: 'weather_tool', + arguments: { + city: 'Paris', + }, + }, + }, + ], + }); + chatComplete.mockResolvedValue(response); + + await structuredOutputModel.invoke([ + new HumanMessage({ + content: 'What is the weather like in Paris?', + }), + ]); + + expect(chatComplete).toHaveBeenCalledTimes(1); + expect(chatComplete).toHaveBeenCalledWith({ + connectorId: connector.connectorId, + messages: [ + { + role: MessageRole.User, + content: 'What is the weather like in Paris?', + }, + ], + toolChoice: { + function: 'weather_tool', + }, + tools: { + weather_tool: { + description: 'Use to get the weather', + schema: { + properties: { + city: { + description: 'The city to get the weather for', + type: 'string', + }, + zipCode: { + description: 'The zipCode to get the weather for', + type: 'number', + }, + }, + required: ['city'], + type: 'object', + }, + }, + }, + stream: false, + }); + }); + + it('returns the correct tool call', async () => { + const chatModel = new InferenceChatModel({ + logger, + chatComplete, + connector, + }); + + const structuredOutputModel = chatModel.withStructuredOutput( + z + .object({ + city: z.string().describe('The city to get the weather for'), + zipCode: z.number().optional().describe('The zipCode to get the weather for'), + }) + .describe('Use to get the weather'), + { name: 'weather_tool' } + ); + + const response = createResponse({ + content: '', + toolCalls: [ + { + toolCallId: 'myToolCallId', + function: { + name: 'weather_tool', + arguments: { + city: 'Paris', + }, + }, + }, + ], + }); + chatComplete.mockResolvedValue(response); + + const output = await structuredOutputModel.invoke([ + new HumanMessage({ + content: 'What is the weather like in Paris?', + }), + ]); + + expect(output).toEqual({ city: 'Paris' }); + }); + }); +}); diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts new file mode 100644 index 0000000000000..ad44b9d6a94b3 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/inference_chat_model.ts @@ -0,0 +1,399 @@ +/* + * Copyright 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 { z } from '@kbn/zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; +import { + BaseChatModel, + type BaseChatModelParams, + type BaseChatModelCallOptions, + type BindToolsInput, + type LangSmithParams, +} from '@langchain/core/language_models/chat_models'; +import type { + BaseLanguageModelInput, + StructuredOutputMethodOptions, + ToolDefinition, +} from '@langchain/core/language_models/base'; +import type { BaseMessage, AIMessageChunk } from '@langchain/core/messages'; +import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'; +import { isZodSchema } from '@langchain/core/utils/types'; +import { ChatGenerationChunk, ChatResult, ChatGeneration } from '@langchain/core/outputs'; +import { OutputParserException } from '@langchain/core/output_parsers'; +import { + Runnable, + RunnablePassthrough, + RunnableSequence, + RunnableLambda, +} from '@langchain/core/runnables'; +import type { Logger } from '@kbn/logging'; +import { + InferenceConnector, + ChatCompleteAPI, + ChatCompleteOptions, + ChatCompleteCompositeResponse, + FunctionCallingMode, + ToolOptions, + isChatCompletionChunkEvent, + isChatCompletionTokenCountEvent, + isToolValidationError, + getConnectorDefaultModel, + getConnectorProvider, +} from '@kbn/inference-common'; +import type { ToolChoice } from './types'; +import { toAsyncIterator, wrapInferenceError } from './utils'; +import { + messagesToInference, + toolDefinitionToInference, + toolChoiceToInference, +} from './to_inference'; +import { + completionChunkToLangchain, + tokenCountChunkToLangchain, + responseToLangchainMessage, +} from './from_inference'; + +export interface InferenceChatModelParams extends BaseChatModelParams { + connector: InferenceConnector; + chatComplete: ChatCompleteAPI; + logger: Logger; + functionCallingMode?: FunctionCallingMode; + temperature?: number; + model?: string; +} + +export interface InferenceChatModelCallOptions extends BaseChatModelCallOptions { + functionCallingMode?: FunctionCallingMode; + tools?: BindToolsInput[]; + tool_choice?: ToolChoice; + temperature?: number; + model?: string; +} + +type InvocationParams = Omit; + +/** + * Langchain chatModel utilizing the inference API under the hood for communication with the LLM. + * + * @example + * ```ts + * const chatModel = new InferenceChatModel({ + * chatComplete: inference.chatComplete, + * connector: someConnector, + * logger: myPluginLogger + * }); + * + * // just use it as another langchain chatModel + * ``` + */ +export class InferenceChatModel extends BaseChatModel { + private readonly chatComplete: ChatCompleteAPI; + private readonly connector: InferenceConnector; + // @ts-ignore unused for now + private readonly logger: Logger; + + protected temperature?: number; + protected functionCallingMode?: FunctionCallingMode; + protected model?: string; + + constructor(args: InferenceChatModelParams) { + super(args); + this.chatComplete = args.chatComplete; + this.connector = args.connector; + this.logger = args.logger; + + this.temperature = args.temperature; + this.functionCallingMode = args.functionCallingMode; + this.model = args.model; + } + + static lc_name() { + return 'InferenceChatModel'; + } + + public get callKeys() { + return [ + ...super.callKeys, + 'functionCallingMode', + 'tools', + 'tool_choice', + 'temperature', + 'model', + ]; + } + + getConnector() { + return this.connector; + } + + _llmType() { + // TODO bedrock / gemini / openai / inference ? + // ideally retrieve info from the inference API / connector + // but the method is sync and we can't retrieve this info synchronously, so... + return 'inference'; + } + + _modelType() { + // TODO + // Some agent / langchain stuff have behavior depending on the model type, so we use base_chat_model for now. + // See: https://github.com/langchain-ai/langchainjs/blob/fb699647a310c620140842776f4a7432c53e02fa/langchain/src/agents/openai/index.ts#L185 + return 'base_chat_model'; + } + + _identifyingParams() { + return { + model_name: this.model ?? getConnectorDefaultModel(this.connector), + ...this.invocationParams({}), + }; + } + + identifyingParams() { + return this._identifyingParams(); + } + + getLsParams(options: this['ParsedCallOptions']): LangSmithParams { + const params = this.invocationParams(options); + return { + ls_provider: `inference-${getConnectorProvider(this.connector)}`, + ls_model_name: options.model ?? this.model ?? getConnectorDefaultModel(this.connector), + ls_model_type: 'chat', + ls_temperature: params.temperature ?? this.temperature ?? undefined, + }; + } + + override bindTools(tools: BindToolsInput[], kwargs?: Partial) { + // conversion will be done at call time for simplicity's sake + // so we just need to implement this method with the default behavior to support tools + return this.bind({ + tools, + ...kwargs, + } as Partial); + } + + invocationParams(options: this['ParsedCallOptions']): InvocationParams { + return { + connectorId: this.connector.connectorId, + functionCalling: options.functionCallingMode ?? this.functionCallingMode, + modelName: options.model ?? this.model, + temperature: options.temperature ?? this.temperature, + tools: options.tools ? toolDefinitionToInference(options.tools) : undefined, + toolChoice: options.tool_choice ? toolChoiceToInference(options.tool_choice) : undefined, + abortSignal: options.signal, + }; + } + + async completionWithRetry( + request: ChatCompleteOptions + ): Promise>; + async completionWithRetry( + request: ChatCompleteOptions + ): Promise>; + async completionWithRetry( + request: ChatCompleteOptions + ): Promise> { + return this.caller.call(async () => { + try { + return await this.chatComplete(request); + } catch (e) { + throw wrapInferenceError(e); + } + }); + } + + async _generate( + baseMessages: BaseMessage[], + options: this['ParsedCallOptions'], + runManager?: CallbackManagerForLLMRun + ): Promise { + const { system, messages } = messagesToInference(baseMessages); + + let response: Awaited>; + try { + response = await this.completionWithRetry({ + ...this.invocationParams(options), + system, + messages, + stream: false, + }); + } catch (e) { + // convert tool validation to output parser exception + // for structured output calls + if (isToolValidationError(e) && e.meta.toolCalls) { + throw new OutputParserException( + `Failed to parse. Error: ${e.message}`, + JSON.stringify(e.meta.toolCalls) + ); + } + throw e; + } + + const generations: ChatGeneration[] = []; + generations.push({ + text: response.content, + message: responseToLangchainMessage(response), + }); + + return { + generations, + llmOutput: { + ...(response.tokens + ? { + tokenUsage: { + promptTokens: response.tokens.prompt, + completionTokens: response.tokens.completion, + totalTokens: response.tokens.total, + }, + } + : {}), + }, + }; + } + + async *_streamResponseChunks( + baseMessages: BaseMessage[], + options: this['ParsedCallOptions'], + runManager?: CallbackManagerForLLMRun + ): AsyncGenerator { + const { system, messages } = messagesToInference(baseMessages); + const response$ = await this.completionWithRetry({ + ...this.invocationParams(options), + system, + messages, + stream: true as const, + } as ChatCompleteOptions); + + const responseIterator = toAsyncIterator(response$); + for await (const event of responseIterator) { + if (isChatCompletionChunkEvent(event)) { + const chunk = completionChunkToLangchain(event); + const generationChunk = new ChatGenerationChunk({ + message: chunk, + text: event.content, + generationInfo: {}, + }); + + yield generationChunk; + await runManager?.handleLLMNewToken( + generationChunk.text ?? '', + { prompt: 0, completion: 0 }, + undefined, + undefined, + undefined, + { chunk: generationChunk } + ); + } + + if (isChatCompletionTokenCountEvent(event)) { + const chunk = tokenCountChunkToLangchain(event); + const generationChunk = new ChatGenerationChunk({ + text: '', + message: chunk, + }); + yield generationChunk; + } + + if (options.signal?.aborted) { + throw new Error('AbortError'); + } + } + } + + withStructuredOutput = Record>( + outputSchema: z.ZodType | Record, + config?: StructuredOutputMethodOptions + ): Runnable; + withStructuredOutput = Record>( + outputSchema: z.ZodType | Record, + config?: StructuredOutputMethodOptions + ): Runnable; + withStructuredOutput = Record>( + outputSchema: z.ZodType | Record, + config?: StructuredOutputMethodOptions + ): + | Runnable + | Runnable { + const schema: z.ZodType | Record = outputSchema; + const name = config?.name; + const description = schema.description ?? 'A function available to call.'; + const includeRaw = config?.includeRaw; + + let functionName = name ?? 'extract'; + let tools: ToolDefinition[]; + if (isZodSchema(schema)) { + tools = [ + { + type: 'function', + function: { + name: functionName, + description, + parameters: zodToJsonSchema(schema), + }, + }, + ]; + } else { + if ('name' in schema) { + functionName = schema.name; + } + tools = [ + { + type: 'function', + function: { + name: functionName, + description, + parameters: schema, + }, + }, + ]; + } + + const llm = this.bindTools(tools, { tool_choice: functionName }); + + const outputParser = RunnableLambda.from( + (input: AIMessageChunk): RunOutput => { + if (!input.tool_calls || input.tool_calls.length === 0) { + throw new Error('No tool calls found in the response.'); + } + const toolCall = input.tool_calls.find((tc) => tc.name === functionName); + if (!toolCall) { + throw new Error(`No tool call found with name ${functionName}.`); + } + return toolCall.args as RunOutput; + } + ); + + if (!includeRaw) { + return llm.pipe(outputParser).withConfig({ + runName: 'StructuredOutput', + }) as Runnable; + } + + const parserAssign = RunnablePassthrough.assign({ + parsed: (input: any, cfg) => outputParser.invoke(input.raw, cfg), + }); + const parserNone = RunnablePassthrough.assign({ + parsed: () => null, + }); + const parsedWithFallback = parserAssign.withFallbacks({ + fallbacks: [parserNone], + }); + return RunnableSequence.from([ + { + raw: llm, + }, + parsedWithFallback, + ]).withConfig({ + runName: 'StructuredOutputRunnable', + }); + } + + // I have no idea what this is really doing or when this is called, + // but most chatModels implement it while returning an empty object or array, + // so I figured we should do the same + _combineLLMOutput() { + return {}; + } +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/index.ts new file mode 100644 index 0000000000000..2e4272990b514 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { messagesToInference } from './messages'; +export { toolDefinitionToInference, toolChoiceToInference } from './tools'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/messages.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/messages.ts new file mode 100644 index 0000000000000..881b8de4520b9 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/messages.ts @@ -0,0 +1,133 @@ +/* + * Copyright 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 { + Message as InferenceMessage, + MessageContent as InferenceMessageContent, + MessageRole, + ToolCall as ToolCallInference, + generateFakeToolCallId, +} from '@kbn/inference-common'; +import { + type BaseMessage, + type AIMessage, + type OpenAIToolCall, + isAIMessage, + isFunctionMessage, + isHumanMessage, + isSystemMessage, + isToolMessage, +} from '@langchain/core/messages'; +import { isMessageContentText, isMessageContentImageUrl } from '../utils/langchain'; + +// type is not exposed from the lib... +type ToolCall = Required['tool_calls'][number]; + +export const messagesToInference = (messages: BaseMessage[]) => { + return messages.reduce( + (output, message) => { + if (isSystemMessage(message)) { + const content = extractMessageTextContent(message); + output.system = output.system ? `${output.system}\n${content}` : content; + } + if (isHumanMessage(message)) { + output.messages.push({ + role: MessageRole.User, + content: convertMessageContent(message), + }); + } + if (isAIMessage(message)) { + output.messages.push({ + role: MessageRole.Assistant, + content: extractMessageTextContent(message), + toolCalls: message.tool_calls?.length + ? message.tool_calls.map(toolCallToInference) + : message.additional_kwargs?.tool_calls?.length + ? message.additional_kwargs.tool_calls.map(legacyToolCallToInference) + : undefined, + }); + } + if (isToolMessage(message)) { + output.messages.push({ + role: MessageRole.Tool, + // langchain does not have the function name on tool messages + name: message.tool_call_id, + toolCallId: message.tool_call_id, + response: message.content, + }); + } + if (isFunctionMessage(message) && message.additional_kwargs.function_call) { + output.messages.push({ + role: MessageRole.Tool, + name: message.additional_kwargs.function_call.name, + toolCallId: generateFakeToolCallId(), + response: message.content, + }); + } + + return output; + }, + { messages: [], system: undefined } as { + messages: InferenceMessage[]; + system: string | undefined; + } + ); +}; + +const toolCallToInference = (toolCall: ToolCall): ToolCallInference => { + return { + toolCallId: toolCall.id ?? generateFakeToolCallId(), + function: { + name: toolCall.name, + arguments: toolCall.args, + }, + }; +}; + +const legacyToolCallToInference = (toolCall: OpenAIToolCall): ToolCallInference => { + return { + toolCallId: toolCall.id, + function: { + name: toolCall.function.name, + arguments: { args: toolCall.function.arguments }, + }, + }; +}; + +const extractMessageTextContent = (message: BaseMessage): string => { + if (typeof message.content === 'string') { + return message.content; + } + return message.content + .filter(isMessageContentText) + .map((part) => part.text) + .join('\n'); +}; + +const convertMessageContent = (message: BaseMessage): InferenceMessageContent => { + if (typeof message.content === 'string') { + return message.content; + } + return message.content.reduce((messages, part) => { + if (isMessageContentText(part)) { + messages.push({ + type: 'text', + text: part.text, + }); + } else if (isMessageContentImageUrl(part)) { + const imageUrl = typeof part.image_url === 'string' ? part.image_url : part.image_url.url; + messages.push({ + type: 'image', + source: { + data: imageUrl, + mimeType: '', + }, + }); + } + return messages; + }, [] as Exclude); +}; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/tools.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/tools.ts new file mode 100644 index 0000000000000..0413fba36d01e --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/to_inference/tools.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { pick } from 'lodash'; +import type { ZodSchema } from '@kbn/zod'; +import { zodToJsonSchema } from 'zod-to-json-schema'; +import { type BindToolsInput } from '@langchain/core/language_models/chat_models'; +import { ToolDefinition } from '@langchain/core/language_models/base'; +import { isLangChainTool } from '@langchain/core/utils/function_calling'; +import { isZodSchema } from '@langchain/core/utils/types'; +import { + ToolDefinition as ToolDefinitionInference, + ToolChoice as ToolChoiceInference, + ToolChoiceType, + ToolSchema, +} from '@kbn/inference-common'; +import type { ToolChoice } from '../types'; + +export const toolDefinitionToInference = ( + tools: BindToolsInput[] +): Record => { + const definitions: Record = {}; + tools.forEach((tool) => { + if (isLangChainTool(tool)) { + definitions[tool.name] = { + description: tool.description ?? tool.name, + schema: tool.schema ? zodSchemaToInference(tool.schema) : undefined, + }; + } else if (isToolDefinition(tool)) { + definitions[tool.function.name] = { + description: tool.function.description ?? tool.function.name, + schema: isZodSchema(tool.function.parameters) + ? zodSchemaToInference(tool.function.parameters) + : (pick(tool.function.parameters, ['type', 'properties', 'required']) as ToolSchema), + }; + } + }); + return definitions; +}; + +export const toolChoiceToInference = (toolChoice: ToolChoice): ToolChoiceInference => { + if (toolChoice === 'any') { + return ToolChoiceType.required; + } + if (toolChoice === 'auto') { + return ToolChoiceType.auto; + } + if (toolChoice === 'none') { + return ToolChoiceType.none; + } + return { + function: toolChoice, + }; +}; + +function isToolDefinition(def: BindToolsInput): def is ToolDefinition { + return 'type' in def && def.type === 'function' && 'function' in def && typeof def === 'object'; +} + +function zodSchemaToInference(schema: ZodSchema): ToolSchema { + return pick(zodToJsonSchema(schema), ['type', 'properties', 'required']) as ToolSchema; +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/types.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/types.ts new file mode 100644 index 0000000000000..79bb8388e94dc --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/types.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export type ToolChoice = string | 'any' | 'auto' | 'none'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/index.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/index.ts new file mode 100644 index 0000000000000..2922308898424 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/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. + */ + +export { isMessageContentImageUrl, isMessageContentText } from './langchain'; +export { wrapInferenceError } from './wrap_inference_error'; +export { toAsyncIterator } from './observable_to_generator'; diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/langchain.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/langchain.ts new file mode 100644 index 0000000000000..3e8c7a94d7fef --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/langchain.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + MessageContentComplex, + MessageContentImageUrl, + MessageContentText, +} from '@langchain/core/messages'; + +/** + * Type guard for image_url message content + */ +export function isMessageContentImageUrl( + content: MessageContentComplex +): content is MessageContentImageUrl { + return content.type === 'image_url'; +} + +/** + * Type guard for text message content + */ +export function isMessageContentText( + content: MessageContentComplex +): content is MessageContentText { + return content.type === 'text'; +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/observable_to_generator.test.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/observable_to_generator.test.ts new file mode 100644 index 0000000000000..174346b129bcb --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/observable_to_generator.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { of, Observable } from 'rxjs'; +import { toAsyncIterator } from './observable_to_generator'; + +describe('toAsyncIterator', () => { + it('returns an async iterator emitting all the values from the source observable', async () => { + const input = [1, 2, 3, 4, 5]; + const obs$ = of(...input); + + const output = []; + const iterator = toAsyncIterator(obs$); + for await (const event of iterator) { + output.push(event); + } + + expect(output).toEqual(input); + }); + + it('throws an error when the source observable throws', async () => { + const obs$ = new Observable((subscriber) => { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.error(new Error('something went wrong')); + }); + + const output: number[] = []; + const iterator = toAsyncIterator(obs$); + + await expect(async () => { + for await (const event of iterator) { + output.push(event); + } + }).rejects.toThrowErrorMatchingInlineSnapshot(`"something went wrong"`); + + expect(output).toEqual([1, 2, 3]); + }); +}); diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/observable_to_generator.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/observable_to_generator.ts new file mode 100644 index 0000000000000..3d8d94b345fc4 --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/observable_to_generator.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Observable } from 'rxjs'; + +/** + * Convert an Observable into an async iterator. + * (don't ask, langchain is using async iterators for stream mode...) + */ +export function toAsyncIterator(observable: Observable): AsyncIterableIterator { + let resolve: ((value: IteratorResult) => void) | null = null; + let reject: ((reason?: any) => void) | null = null; + + const queue: Array> = []; + let done = false; + + const subscription = observable.subscribe({ + next(value) { + if (resolve) { + resolve({ value, done: false }); + resolve = null; + } else { + queue.push({ value, done: false }); + } + }, + error(err) { + if (reject) { + reject(err); + reject = null; + } else { + queue.push(Promise.reject(err) as any); // Queue an error + } + }, + complete() { + done = true; + if (resolve) { + resolve({ value: undefined, done: true }); + resolve = null; + } + }, + }); + + return { + [Symbol.asyncIterator]() { + return this; + }, + next() { + if (queue.length > 0) { + return Promise.resolve(queue.shift()!); + } + + if (done) { + return Promise.resolve({ value: undefined, done: true }); + } + + return new Promise>((res, rej) => { + resolve = res; + reject = rej; + }); + }, + return() { + subscription.unsubscribe(); + return Promise.resolve({ value: undefined, done: true }); + }, + throw(error?: any) { + subscription.unsubscribe(); + return Promise.reject(error); + }, + }; +} diff --git a/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/wrap_inference_error.ts b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/wrap_inference_error.ts new file mode 100644 index 0000000000000..a486122dfa86b --- /dev/null +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/src/chat_model/utils/wrap_inference_error.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const wrapInferenceError = (error: any) => { + // TODO maybe at some point we may want to add the errors likes as done in the following models + // however, only a very small subset of chat models are doing this, so I don't think it's strictly necessary. + // https://github.com/langchain-ai/langchainjs/blob/ff0dc580a71268b098e5ac2ee68b7d98317727ed/libs/langchain-openai/src/utils/openai.ts + // https://github.com/langchain-ai/langchainjs/blob/ff0dc580a71268b098e5ac2ee68b7d98317727ed/libs/langchain-anthropic/src/utils/errors.ts + return error; +}; diff --git a/src/platform/packages/private/serverless/project_switcher/tsconfig.json b/x-pack/platform/packages/shared/ai-infra/inference-langchain/tsconfig.json similarity index 63% rename from src/platform/packages/private/serverless/project_switcher/tsconfig.json rename to x-pack/platform/packages/shared/ai-infra/inference-langchain/tsconfig.json index d477303d65059..11a66addb4384 100644 --- a/src/platform/packages/private/serverless/project_switcher/tsconfig.json +++ b/x-pack/platform/packages/shared/ai-infra/inference-langchain/tsconfig.json @@ -4,20 +4,19 @@ "outDir": "target/types", "types": [ "jest", - "node", - "react", - "@kbn/ambient-ui-types" + "node" ] }, "include": [ "**/*.ts", - "**/*.tsx", ], "exclude": [ "target/**/*" ], "kbn_references": [ - "@kbn/shared-ux-storybook-mock", - "@kbn/serverless-types", + "@kbn/inference-common", + "@kbn/zod", + "@kbn/logging", + "@kbn/logging-mocks" ] } diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock.ts index bbc19d0566d70..65beab7a9edb3 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock.ts @@ -7,7 +7,7 @@ import { ContentReferencesStore } from '../../types'; -export const contentReferencesStoreFactoryMock: () => ContentReferencesStore = jest +export const newContentReferencesStoreMock: () => ContentReferencesStore = jest .fn() .mockReturnValue({ add: jest.fn().mockImplementation((creator: Parameters[0]) => { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store_factory.test.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store.test.ts similarity index 90% rename from x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store_factory.test.ts rename to x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store.test.ts index c3e848ee66d5c..7346ee7f28765 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store_factory.test.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store.test.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { contentReferencesStoreFactory } from './content_references_store_factory'; +import { newContentReferencesStore } from './content_references_store'; import { securityAlertsPageReference } from '../references'; import { ContentReferencesStore } from '../types'; -describe('contentReferencesStoreFactory', () => { +describe('newContentReferencesStore', () => { let contentReferencesStore: ContentReferencesStore; beforeEach(() => { - contentReferencesStore = contentReferencesStoreFactory(); + contentReferencesStore = newContentReferencesStore(); }); it('adds multiple content reference', async () => { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store_factory.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store.ts similarity index 94% rename from x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store_factory.ts rename to x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store.ts index b1b78263a31f3..e68df0f39467b 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store_factory.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/content_references_store.ts @@ -14,7 +14,7 @@ const CONTENT_REFERENCE_ID_ALPHABET = /** * Creates a new ContentReferencesStore used for storing references (also known as citations) */ -export const contentReferencesStoreFactory: () => ContentReferencesStore = () => { +export const newContentReferencesStore: () => ContentReferencesStore = () => { const store = new Map(); const add: ContentReferencesStore['add'] = (creator) => { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/prune_content_references.test.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/prune_content_references.test.ts index 5f6184144315c..7c47c7767e12a 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/prune_content_references.test.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/content_references_store/prune_content_references.test.ts @@ -9,12 +9,12 @@ import { pruneContentReferences } from './prune_content_references'; import { securityAlertsPageReference } from '../references'; import { contentReferenceBlock } from '../references/utils'; import { ContentReferencesStore } from '../types'; -import { contentReferencesStoreFactory } from './content_references_store_factory'; +import { newContentReferencesStore } from './content_references_store'; describe('pruneContentReferences', () => { let contentReferencesStore: ContentReferencesStore; beforeEach(() => { - contentReferencesStore = contentReferencesStoreFactory(); + contentReferencesStore = newContentReferencesStore(); }); it('prunes content references correctly', async () => { diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/index.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/index.ts index ca9afcbb76bbe..fa9872f316966 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/index.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/impl/content_references/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export { contentReferencesStoreFactory } from './content_references_store/content_references_store_factory'; +export { newContentReferencesStore } from './content_references_store/content_references_store'; export { pruneContentReferences } from './content_references_store/prune_content_references'; export { securityAlertReference, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/index.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/index.ts index 6c5e52df671e1..57ff1a69830cc 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant-common/index.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant-common/index.ts @@ -23,7 +23,7 @@ export { } from './impl/data_anonymization/helpers'; export { - contentReferencesStoreFactory, + newContentReferencesStore, securityAlertReference, knowledgeBaseReference, securityAlertsPageReference, diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx index fbaa00504afbc..95085ac8ef8d6 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/index.tsx @@ -50,6 +50,9 @@ interface OwnProps { } type Props = OwnProps; + +export const AI_ASSISTANT_SETTINGS_MENU_CONTAINER_ID = 'aiAssistantSettingsMenuContainer'; + /** * Renders the header of the Elastic AI Assistant. * Provide a user interface for selecting and managing conversations, @@ -170,7 +173,7 @@ export const AssistantHeader: React.FC = ({ onConnectorSelected={onConversationChange} /> - + diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts index 5589b6a274531..d38eb73b28939 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/assistant_header/translations.ts @@ -66,7 +66,7 @@ export const SHOW_REAL_VALUES = i18n.translate( export const ANONYMIZE_VALUES = i18n.translate( 'xpack.elasticAssistant.assistant.settings.anonymizeValues', { - defaultMessage: 'Show anonymize values', + defaultMessage: 'Show anonymized values', } ); @@ -89,7 +89,7 @@ const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; export const ANONYMIZE_VALUES_TOOLTIP = i18n.translate( 'xpack.elasticAssistant.assistant.settings.anonymizeValues.tooltip', { - values: { keyboardShortcut: isMac ? '⌥ a' : 'Alt a' }, + values: { keyboardShortcut: isMac ? '⌥ + a' : 'Alt + a' }, defaultMessage: 'Toggle to reveal or hide field values in your chat stream. The data sent to the LLM is still anonymized based on settings in the Anonymization panel. Keyboard shortcut: {keyboardShortcut}', } @@ -98,7 +98,7 @@ export const ANONYMIZE_VALUES_TOOLTIP = i18n.translate( export const SHOW_CITATIONS_TOOLTIP = i18n.translate( 'xpack.elasticAssistant.assistant.settings.showCitationsLabel.tooltip', { - values: { keyboardShortcut: isMac ? '⌥ c' : 'Alt c' }, + values: { keyboardShortcut: isMac ? '⌥ + c' : 'Alt + c' }, defaultMessage: 'Keyboard shortcut: {keyboardShortcut}', } ); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx index 0ca54a878e813..399739099109c 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/assistant/index.tsx @@ -50,6 +50,7 @@ import { ConnectorMissingCallout } from '../connectorland/connector_missing_call import { ConversationSidePanel } from './conversations/conversation_sidepanel'; import { SelectedPromptContexts } from './prompt_editor/selected_prompt_contexts'; import { AssistantHeader } from './assistant_header'; +import { AnonymizedValuesAndCitationsTour } from '../tour/anonymized_values_and_citations_tour'; export const CONVERSATION_SIDE_PANEL_WIDTH = 220; @@ -448,196 +449,201 @@ const AssistantComponent: React.FC = ({ ); return ( - - {chatHistoryVisible && ( - - - + <> + {contentReferencesEnabled && ( + )} - - - + {chatHistoryVisible && ( + - + + )} + + + - - - - {/* Create portals for each EuiCodeBlock to add the `Investigate in Timeline` action */} - {createCodeBlockPortals()} - - div { - display: flex; - flex-direction: column; - align-items: stretch; - - > .euiFlyoutBody__banner { - overflow-x: unset; - } + max-width: 100%; + `} + > + + + + {/* Create portals for each EuiCodeBlock to add the `Investigate in Timeline` action */} + {createCodeBlockPortals()} + + .euiFlyoutBody__overflowContent { + > div { display: flex; - flex: 1; - overflow: auto; + flex-direction: column; + align-items: stretch; + + > .euiFlyoutBody__banner { + overflow-x: unset; + } + + > .euiFlyoutBody__overflowContent { + display: flex; + flex: 1; + overflow: auto; + } } + `} + banner={ + !isDisabled && + showMissingConnectorCallout && + isFetchedConnectors && ( + 0} + isSettingsModalVisible={isSettingsModalVisible} + setIsSettingsModalVisible={setIsSettingsModalVisible} + /> + ) } - `} - banner={ - !isDisabled && - showMissingConnectorCallout && - isFetchedConnectors && ( - 0} - isSettingsModalVisible={isSettingsModalVisible} - setIsSettingsModalVisible={setIsSettingsModalVisible} - /> - ) - } - > - - - - + + + - {!isDisabled && - Object.keys(promptContexts).length !== selectedPromptContextsCount && ( - - - <> - - {Object.keys(promptContexts).length > 0 && } - + + {!isDisabled && + Object.keys(promptContexts).length !== selectedPromptContextsCount && ( + + + <> + + {Object.keys(promptContexts).length > 0 && } + + + + )} + + + {Object.keys(selectedPromptContexts).length ? ( + + - - )} + ) : null} - - {Object.keys(selectedPromptContexts).length ? ( - - ) : null} - - - - - - - - {!isDisabled && ( - - + - )} - - - - - - + + {!isDisabled && ( + + + + )} + + + + + + + ); }; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/mock/conversation.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/mock/conversation.ts index bde74fe8d744f..5721e4788154b 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/mock/conversation.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/mock/conversation.ts @@ -6,7 +6,7 @@ */ import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/openai/constants'; -import { Conversation } from '../..'; +import { ClientMessage, Conversation } from '../..'; export const alertConvo: Conversation = { id: '', @@ -33,6 +33,20 @@ export const alertConvo: Conversation = { }, }; +export const messageWithContentReferences: ClientMessage = { + content: 'You have 1 alert.{reference(abcde)}', + role: 'user', + timestamp: '2023-03-19T18:59:18.174Z', + metadata: { + contentReferences: { + abcde: { + id: 'abcde', + type: 'SecurityAlertsPage', + }, + }, + }, +}; + export const emptyWelcomeConvo: Conversation = { id: '', title: 'Welcome', @@ -47,6 +61,11 @@ export const emptyWelcomeConvo: Conversation = { }, }; +export const conversationWithContentReferences: Conversation = { + ...emptyWelcomeConvo, + messages: [messageWithContentReferences], +}; + export const welcomeConvo: Conversation = { ...emptyWelcomeConvo, messages: [ diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.test.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.test.tsx new file mode 100644 index 0000000000000..179b473e55804 --- /dev/null +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.test.tsx @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen, waitFor } from '@testing-library/react'; +import { AnonymizedValuesAndCitationsTour } from '.'; +import React from 'react'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { + alertConvo, + conversationWithContentReferences, + welcomeConvo, +} from '../../mock/conversation'; +import { I18nProvider } from '@kbn/i18n-react'; +import { TourState } from '../knowledge_base'; + +jest.mock('react-use/lib/useLocalStorage', () => jest.fn()); + +jest.mock('lodash', () => ({ + ...jest.requireActual('lodash'), + throttle: jest.fn().mockImplementation((fn) => fn), +})); + +const mockGetItem = jest.fn(); +Object.defineProperty(window, 'localStorage', { + value: { + getItem: (...args: string[]) => mockGetItem(...args), + }, +}); + +const Wrapper = ({ children }: { children?: React.ReactNode }) => ( + +
+
+ {children} +
+ +); + +describe('AnonymizedValuesAndCitationsTour', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + }); + + it('renders tour when there are content references', async () => { + (useLocalStorage as jest.Mock).mockReturnValue([false, jest.fn()]); + + mockGetItem.mockReturnValue( + JSON.stringify({ + currentTourStep: 2, + isTourActive: true, + } as TourState) + ); + + render(, { + wrapper: Wrapper, + }); + + jest.runAllTimers(); + + await waitFor(() => { + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStep')).toBeInTheDocument(); + }); + + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStepPanel')).toBeInTheDocument(); + }); + + it('renders tour when there are replacements', async () => { + (useLocalStorage as jest.Mock).mockReturnValue([false, jest.fn()]); + + mockGetItem.mockReturnValue( + JSON.stringify({ + currentTourStep: 2, + isTourActive: true, + } as TourState) + ); + + render(, { + wrapper: Wrapper, + }); + + jest.runAllTimers(); + + await waitFor(() => { + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStep')).toBeInTheDocument(); + }); + + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStepPanel')).toBeInTheDocument(); + }); + + it('does not render tour if it has already been shown', async () => { + (useLocalStorage as jest.Mock).mockReturnValue([true, jest.fn()]); + + mockGetItem.mockReturnValue( + JSON.stringify({ + currentTourStep: 2, + isTourActive: true, + } as TourState) + ); + + render(, { + wrapper: Wrapper, + }); + + jest.runAllTimers(); + + await waitFor(() => { + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStep')).toBeInTheDocument(); + }); + + expect( + screen.queryByTestId('anonymizedValuesAndCitationsTourStepPanel') + ).not.toBeInTheDocument(); + }); + + it('does not render tour if the knowledge base tour is on step 1', async () => { + (useLocalStorage as jest.Mock).mockReturnValue([false, jest.fn()]); + + mockGetItem.mockReturnValue( + JSON.stringify({ + currentTourStep: 1, + isTourActive: true, + } as TourState) + ); + + render(, { + wrapper: Wrapper, + }); + + jest.runAllTimers(); + + await waitFor(() => { + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStep')).toBeInTheDocument(); + }); + + expect( + screen.queryByTestId('anonymizedValuesAndCitationsTourStepPanel') + ).not.toBeInTheDocument(); + }); + + it('does not render tour if there are no content references or replacements', async () => { + (useLocalStorage as jest.Mock).mockReturnValue([false, jest.fn()]); + + mockGetItem.mockReturnValue( + JSON.stringify({ + currentTourStep: 2, + isTourActive: true, + } as TourState) + ); + + render(, { + wrapper: Wrapper, + }); + + jest.runAllTimers(); + + await waitFor(() => { + expect(screen.getByTestId('anonymizedValuesAndCitationsTourStep')).toBeInTheDocument(); + }); + + expect( + screen.queryByTestId('anonymizedValuesAndCitationsTourStepPanel') + ).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx new file mode 100644 index 0000000000000..da2bbc7469b46 --- /dev/null +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/index.tsx @@ -0,0 +1,91 @@ +/* + * Copyright 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 { EuiTourStep } from '@elastic/eui'; +import React, { useCallback, useEffect, useState } from 'react'; +import { isEmpty, throttle } from 'lodash'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; +import { Conversation } from '../../assistant_context/types'; +import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../const'; +import { anonymizedValuesAndCitationsTourStep1 } from './step_config'; +import { TourState } from '../knowledge_base'; + +interface Props { + conversation: Conversation | undefined; +} + +// Throttles reads from local storage to 1 every 5 seconds. +// This is to prevent excessive reading from local storage. It acts +// as a cache. +const getKnowledgeBaseTourStateThrottled = throttle(() => { + const value = localStorage.getItem(NEW_FEATURES_TOUR_STORAGE_KEYS.KNOWLEDGE_BASE); + if (value) { + return JSON.parse(value) as TourState; + } + return undefined; +}, 5000); + +export const AnonymizedValuesAndCitationsTour: React.FC = ({ conversation }) => { + const [tourCompleted, setTourCompleted] = useLocalStorage( + NEW_FEATURES_TOUR_STORAGE_KEYS.ANONYMIZED_VALUES_AND_CITATIONS, + false + ); + + const [showTour, setShowTour] = useState(false); + + useEffect(() => { + if (showTour || !conversation || tourCompleted) { + return; + } + + const knowledgeBaseTourState = getKnowledgeBaseTourStateThrottled(); + + // If the knowledge base tour is active on this page (i.e. step 1), don't show this tour to prevent overlap. + if (knowledgeBaseTourState?.isTourActive && knowledgeBaseTourState?.currentTourStep === 1) { + return; + } + + const containsContentReferences = conversation.messages.some( + (message) => !isEmpty(message.metadata?.contentReferences) + ); + const containsReplacements = !isEmpty(conversation.replacements); + + if (containsContentReferences || containsReplacements) { + const timer = setTimeout(() => { + setShowTour(true); + }, 1000); + + return () => { + clearTimeout(timer); + }; + } + }, [conversation, tourCompleted, showTour]); + + const finishTour = useCallback(() => { + setTourCompleted(true); + setShowTour(false); + }, [setTourCompleted, setShowTour]); + + return ( + + ); +}; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx new file mode 100644 index 0000000000000..dad5326f0be2c --- /dev/null +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/anonymized_values_and_citations_tour/step_config.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiText, EuiSpacer } from '@elastic/eui'; +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { AI_ASSISTANT_SETTINGS_MENU_CONTAINER_ID } from '../../assistant/assistant_header'; + +const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; + +export const anonymizedValuesAndCitationsTourStep1 = { + title: ( + + ), + subTitle: ( + + ), + anchor: `#${AI_ASSISTANT_SETTINGS_MENU_CONTAINER_ID}`, + content: ( + + {str}, + }} + /> + + {str}, + }} + /> + + ), +}; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/const.ts b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/const.ts index 1c79500792ba6..91380d73cca5e 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/const.ts +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/const.ts @@ -7,4 +7,6 @@ export const NEW_FEATURES_TOUR_STORAGE_KEYS = { KNOWLEDGE_BASE: 'elasticAssistant.knowledgeBase.newFeaturesTour.v8.16', + ANONYMIZED_VALUES_AND_CITATIONS: + 'elasticAssistant.anonymizedValuesAndCitationsTourCompleted.v8.18', }; diff --git a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/knowledge_base/index.tsx b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/knowledge_base/index.tsx index 8d71b4491a2fd..bc2c8b344fbfd 100644 --- a/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/knowledge_base/index.tsx +++ b/x-pack/platform/packages/shared/kbn-elastic-assistant/impl/tour/knowledge_base/index.tsx @@ -20,7 +20,7 @@ import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../const'; import { knowledgeBaseTourStepOne, tourConfig } from './step_config'; import * as i18n from './translations'; -interface TourState { +export interface TourState { currentTourStep: number; isTourActive: boolean; } diff --git a/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/components/providers/render_service_provider/service_provider.tsx b/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/components/providers/render_service_provider/service_provider.tsx index e15fa03eaf15c..f5e379f94e85b 100644 --- a/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/components/providers/render_service_provider/service_provider.tsx +++ b/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/components/providers/render_service_provider/service_provider.tsx @@ -21,6 +21,7 @@ import amazonBedrockIcon from '../assets/images/amazon_bedrock.svg'; import anthropicIcon from '../assets/images/anthropic.svg'; import alibabaCloudIcon from '../assets/images/alibaba_cloud.svg'; import ibmWatsonxIcon from '../assets/images/ibm_watsonx.svg'; +import jinaAIIcon from '../assets/images/jinaai.svg'; interface ServiceProviderProps { providerKey: ServiceProviderKeys; @@ -106,6 +107,11 @@ export const SERVICE_PROVIDERS: Record = ({ providerKey }) => { diff --git a/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/constants.ts b/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/constants.ts index d0a3c3a24d7fc..e2afa34dd7d7b 100644 --- a/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/constants.ts +++ b/x-pack/platform/packages/shared/kbn-inference-endpoint-ui-common/src/constants.ts @@ -20,6 +20,7 @@ export enum ServiceProviderKeys { anthropic = 'anthropic', watsonxai = 'watsonxai', 'alibabacloud-ai-search' = 'alibabacloud-ai-search', + jinaai = 'jinaai', } export const DEFAULT_TASK_TYPE = 'completion'; diff --git a/x-pack/platform/packages/shared/kbn-slo-schema/src/rest_specs/slo.ts b/x-pack/platform/packages/shared/kbn-slo-schema/src/rest_specs/slo.ts index 6fc093e054055..62f82a2a161ca 100644 --- a/x-pack/platform/packages/shared/kbn-slo-schema/src/rest_specs/slo.ts +++ b/x-pack/platform/packages/shared/kbn-slo-schema/src/rest_specs/slo.ts @@ -17,9 +17,8 @@ import { const sloWithDataResponseSchema = t.intersection([ sloDefinitionSchema, - t.type({ summary: summarySchema, groupings: groupingsSchema }), + t.type({ summary: summarySchema, groupings: groupingsSchema, instanceId: allOrAnyString }), t.partial({ - instanceId: allOrAnyString, meta: metaSchema, remote: remoteSchema, }), diff --git a/x-pack/platform/packages/shared/security/plugin_types_common/index.ts b/x-pack/platform/packages/shared/security/plugin_types_common/index.ts index c435dae0baedc..c5210877f28ac 100644 --- a/x-pack/platform/packages/shared/security/plugin_types_common/index.ts +++ b/x-pack/platform/packages/shared/security/plugin_types_common/index.ts @@ -47,3 +47,5 @@ export type { CategorizedApiKey, ApiKeyAggregations, } from './src/api_keys/api_key'; + +export { ApiOperation } from './src/authorization'; diff --git a/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/api.ts b/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/api.ts new file mode 100644 index 0000000000000..dbb4f0bf0ba4d --- /dev/null +++ b/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/api.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export enum ApiOperation { + Read = 'read', + Create = 'create', + Update = 'update', + Delete = 'delete', + Manage = 'manage', +} diff --git a/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/index.ts b/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/index.ts index 4079bf81ee441..7cbcfd7261957 100644 --- a/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/index.ts +++ b/x-pack/platform/packages/shared/security/plugin_types_common/src/authorization/index.ts @@ -17,3 +17,4 @@ export type { RoleRemoteIndexPrivilege, RoleRemoteClusterPrivilege, } from './role'; +export { ApiOperation } from './api'; diff --git a/x-pack/platform/packages/shared/security/plugin_types_server/index.ts b/x-pack/platform/packages/shared/security/plugin_types_server/index.ts index 4f213fc6c9920..ac362ed3a4cf9 100644 --- a/x-pack/platform/packages/shared/security/plugin_types_server/index.ts +++ b/x-pack/platform/packages/shared/security/plugin_types_server/index.ts @@ -89,4 +89,3 @@ export { getRestApiKeyWithKibanaPrivilegesSchema, } from './src/authentication'; export { getKibanaRoleSchema, elasticsearchRoleSchema, GLOBAL_RESOURCE } from './src/authorization'; -export { ApiOperation } from './src/authorization'; diff --git a/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/api.ts b/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/api.ts index 01fa535a1a0d5..e689e4c5937fd 100644 --- a/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/api.ts +++ b/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/api.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { ApiOperation } from '@kbn/security-plugin-types-common'; + export interface ApiActions { get(operation: ApiOperation, subject: string): string; @@ -14,11 +16,3 @@ export interface ApiActions { get(subject: string): string; actionFromRouteTag(routeTag: string): string; } - -export enum ApiOperation { - Read = 'read', - Create = 'create', - Update = 'update', - Delete = 'delete', - Manage = 'manage', -} diff --git a/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/index.ts b/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/index.ts index baed1cde4457e..6b3993423015f 100644 --- a/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/index.ts +++ b/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/actions/index.ts @@ -8,7 +8,6 @@ export type { Actions } from './actions'; export type { AlertingActions } from './alerting'; export type { ApiActions } from './api'; -export { ApiOperation } from './api'; export type { AppActions } from './app'; export type { CasesActions } from './cases'; export type { SavedObjectActions } from './saved_object'; diff --git a/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/index.ts b/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/index.ts index 0ffa0900fa2d3..8f9e2788753a3 100644 --- a/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/index.ts +++ b/x-pack/platform/packages/shared/security/plugin_types_server/src/authorization/index.ts @@ -15,7 +15,6 @@ export type { SpaceActions, UIActions, } from './actions'; -export { ApiOperation } from './actions'; export type { AuthorizationServiceSetup } from './authorization_service'; export type { CheckPrivilegesOptions, diff --git a/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/index.ts b/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/index.ts index 1462728e756b8..c7f4ab93b3dcf 100644 --- a/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/index.ts +++ b/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/index.ts @@ -11,5 +11,6 @@ export type { SortField, SortDirection, SnapshotListParams } from './snapshot_li export { getListParams, getQueryFromListParams, + escapeString, DEFAULT_SNAPSHOT_LIST_PARAMS, } from './snapshot_list_params'; diff --git a/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.test.ts b/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.test.ts new file mode 100644 index 0000000000000..c50b0ac0c0557 --- /dev/null +++ b/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + DEFAULT_SNAPSHOT_LIST_PARAMS, + escapeString, + getQueryFromListParams, +} from './snapshot_list_params'; + +describe('Snapshot list params', () => { + describe('escapeString', () => { + it('should escape special characters', () => { + expect(escapeString('(special)[]{chars}')).toBe('\\(special\\)[]\\{chars\\}'); + }); + + it('should unescape escaped characters before escape it ', () => { + expect(escapeString('\\(special\\)[]\\{chars\\}')).toBe('\\(special\\)[]\\{chars\\}'); + }); + + it('should handle strings without special characters', () => { + expect(escapeString('no special chars')).toBe('no special chars'); + }); + }); + + describe('getQueryFromListParams', () => { + const schema = {}; + + it('should parse the query', () => { + const listParams = { + ...DEFAULT_SNAPSHOT_LIST_PARAMS, + searchField: 'field', + searchValue: 'value', + }; + + expect(getQueryFromListParams(listParams, schema).text).toBe('field=value'); + }); + + it('should parse the query and escape special characters', () => { + const listParams = { + ...DEFAULT_SNAPSHOT_LIST_PARAMS, + searchField: 'field', + searchValue: '(val)ue', + }; + + expect(getQueryFromListParams(listParams, schema).text).toBe('field=\\(val\\)ue'); + }); + }); +}); diff --git a/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.ts b/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.ts index 8c29a910c2d0c..25e4c1f8326ef 100644 --- a/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.ts +++ b/x-pack/platform/plugins/private/snapshot_restore/public/application/lib/snapshot_list_params.ts @@ -50,6 +50,12 @@ const resetSearchOptions = (listParams: SnapshotListParams): SnapshotListParams searchOperator: undefined, }); +export const escapeString = (inputString: string) => { + return inputString.replace(/\\([{}()\\])|([{}()\\])/g, (match, unescaped, needsEscape) => + unescaped ? match : `\\${needsEscape}` + ); +}; + // to init the query for repository and policyName search passed via url export const getQueryFromListParams = ( listParams: SnapshotListParams, @@ -59,7 +65,8 @@ export const getQueryFromListParams = ( if (!searchField || !searchValue) { return Query.MATCH_ALL; } - return Query.parse(`${searchField}=${searchValue}`, { schema }); + + return Query.parse(`${searchField}=${escapeString(searchValue)}`, { schema }); }; export const getListParams = (listParams: SnapshotListParams, query: Query): SnapshotListParams => { diff --git a/x-pack/platform/plugins/private/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx b/x-pack/platform/plugins/private/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx index c2972a4c0a4d1..62b8859b60901 100644 --- a/x-pack/platform/plugins/private/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx +++ b/x-pack/platform/plugins/private/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx @@ -16,7 +16,12 @@ import { EuiSearchBarOnChangeArgs } from '@elastic/eui/src/components/search_bar import { EuiButton, EuiCallOut, EuiSearchBar, EuiSpacer, Query } from '@elastic/eui'; import { SnapshotDeleteProvider } from '../../../../components'; import { SnapshotDetails } from '../../../../../../common/types'; -import { getQueryFromListParams, SnapshotListParams, getListParams } from '../../../../lib'; +import { + getQueryFromListParams, + SnapshotListParams, + getListParams, + escapeString, +} from '../../../../lib'; const SEARCH_DEBOUNCE_VALUE_MS = 200; @@ -117,6 +122,7 @@ export const SnapshotSearchBar: React.FunctionComponent = ({ })), }, ]; + const reloadButton = ( = ({ const onSearchBarChange = (args: EuiSearchBarOnChangeArgs) => { const { query: changedQuery, error: queryError } = args; - if (queryError) { - setError(queryError); - } else if (changedQuery) { + + if (changedQuery) { + changedQuery.text = escapeString(changedQuery.text); + setError(null); setQuery(changedQuery); if (changedQuery.ast.clauses.length > 1) { @@ -141,6 +148,8 @@ export const SnapshotSearchBar: React.FunctionComponent = ({ } else { setCachedListParams(getListParams(listParams, changedQuery)); } + } else if (queryError) { + setError(queryError); } }; diff --git a/x-pack/platform/plugins/private/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/platform/plugins/private/snapshot_restore/server/routes/api/validate_schemas.ts index 4167c0d972a48..d0861babb1fb4 100644 --- a/x-pack/platform/plugins/private/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/platform/plugins/private/snapshot_restore/server/routes/api/validate_schemas.ts @@ -65,31 +65,13 @@ export const policySchema = schema.object({ // Only validate required settings, everything else is optional const fsRepositorySettings = schema.object({ location: schema.string() }, { unknowns: 'allow' }); -const fsRepositorySchema = schema.object({ - name: schema.string({ maxLength: 1000 }), - type: schema.string(), - settings: fsRepositorySettings, -}); - const readOnlyRepositorySettings = schema.object({ url: schema.string(), }); -const readOnlyRepository = schema.object({ - name: schema.string({ maxLength: 1000 }), - type: schema.string(), - settings: readOnlyRepositorySettings, -}); - // Only validate required settings, everything else is optional const s3RepositorySettings = schema.object({ bucket: schema.string() }, { unknowns: 'allow' }); -const s3Repository = schema.object({ - name: schema.string({ maxLength: 1000 }), - type: schema.string(), - settings: s3RepositorySettings, -}); - // Only validate required settings, everything else is optional const hdsRepositorySettings = schema.object( { @@ -99,30 +81,27 @@ const hdsRepositorySettings = schema.object( { unknowns: 'allow' } ); -const hdsfRepository = schema.object({ - name: schema.string({ maxLength: 1000 }), - type: schema.string(), - settings: hdsRepositorySettings, -}); - const azureRepositorySettings = schema.object({}, { unknowns: 'allow' }); -const azureRepository = schema.object({ - name: schema.string({ maxLength: 1000 }), - type: schema.string(), - settings: azureRepositorySettings, -}); - // Only validate required settings, everything else is optional const gcsRepositorySettings = schema.object({ bucket: schema.string() }, { unknowns: 'allow' }); -const gcsRepository = schema.object({ - name: schema.string({ maxLength: 1000 }), - type: schema.string(), - settings: gcsRepositorySettings, -}); +const sourceRepositorySettings = schema.oneOf([ + fsRepositorySettings, + readOnlyRepositorySettings, + s3RepositorySettings, + hdsRepositorySettings, + azureRepositorySettings, + gcsRepositorySettings, + schema.object( + { + delegateType: schema.string(), + }, + { unknowns: 'allow' } + ), +]); -const sourceRepository = schema.object({ +export const repositorySchema = schema.object({ name: schema.string({ maxLength: 1000 }), type: schema.string(), settings: schema.oneOf([ @@ -132,25 +111,10 @@ const sourceRepository = schema.object({ hdsRepositorySettings, azureRepositorySettings, gcsRepositorySettings, - schema.object( - { - delegateType: schema.string(), - }, - { unknowns: 'allow' } - ), + sourceRepositorySettings, ]), }); -export const repositorySchema = schema.oneOf([ - fsRepositorySchema, - readOnlyRepository, - sourceRepository, - s3Repository, - hdsfRepository, - azureRepository, - gcsRepository, -]); - export const restoreSettingsSchema = schema.object({ indices: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])), renamePattern: schema.maybe(schema.string()), diff --git a/x-pack/platform/plugins/private/translations/translations/fr-FR.json b/x-pack/platform/plugins/private/translations/translations/fr-FR.json index b0be682999d04..af472e57e5709 100644 --- a/x-pack/platform/plugins/private/translations/translations/fr-FR.json +++ b/x-pack/platform/plugins/private/translations/translations/fr-FR.json @@ -31100,7 +31100,6 @@ "xpack.observability.customThreshold.rule.reason.forTheLast": "durée : {duration}", "xpack.observability.customThreshold.rule.reason.group": "groupe : {group}", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "Une description concise de la raison du signalement", - "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery doit être un filtre KQL valide (erreur : {errorMessage})", "xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "Échec de chargement de la source : Aucun client HTTP disponible.", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "Nous n'avons pas pu appliquer les modifications à la configuration des indicateurs. Réessayez plus tard.", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "La mise à jour de la configuration a échoué", @@ -44219,12 +44218,9 @@ "xpack.timelines.dragAndDrop.copyToClipboardTooltip": "Copier dans le Presse-papiers", "xpack.timelines.hoverActions.addToTimeline": "Ajouter à l'investigation de chronologie", "xpack.timelines.hoverActions.addToTimeline.addedFieldMessage": "Ajout effectué de {fieldOrValue} {isTimeline, select, true {à la chronologie} other {au modèle}}", - "xpack.timelines.hoverActions.columnToggleLabel": "Basculer la vue {field} dans le tableau", "xpack.timelines.hoverActions.fieldLabel": "Champ", "xpack.timelines.hoverActions.filterIn": "Inclure", "xpack.timelines.hoverActions.filterOut": "Exclure", - "xpack.timelines.hoverActions.moreActions": "Plus d'actions", - "xpack.timelines.hoverActions.nestedColumnToggleLabel": "Le champ {field} est un objet, et il est composé de champs imbriqués qui peuvent être ajoutés en tant que colonnes", "xpack.timelines.hoverActions.tooltipWithKeyboardShortcut.pressTooltipLabel": "Appuyer", "xpack.timelines.updated": "Mis à jour", "xpack.timelines.updating": "Mise à jour...", diff --git a/x-pack/platform/plugins/private/translations/translations/ja-JP.json b/x-pack/platform/plugins/private/translations/translations/ja-JP.json index f6e3d37dc39c8..b87da69da932a 100644 --- a/x-pack/platform/plugins/private/translations/translations/ja-JP.json +++ b/x-pack/platform/plugins/private/translations/translations/ja-JP.json @@ -30964,7 +30964,6 @@ "xpack.observability.customThreshold.rule.reason.forTheLast": "duration: {duration}", "xpack.observability.customThreshold.rule.reason.group": "グループ:{group}", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "アラートの理由の簡潔な説明", - "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQueryは有効なKQLフィルターでなければなりません(エラー:{errorMessage})", "xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "ソースの読み込みに失敗しました:HTTPクライアントがありません。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "変更をメトリック構成に適用できませんでした。しばらくたってから再試行してください。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "構成の更新が失敗しました", @@ -44073,12 +44072,9 @@ "xpack.timelines.dragAndDrop.copyToClipboardTooltip": "クリップボードにコピー", "xpack.timelines.hoverActions.addToTimeline": "タイムライン調査に追加", "xpack.timelines.hoverActions.addToTimeline.addedFieldMessage": "{fieldOrValue}を{isTimeline, select, true {タイムライン} other {テンプレート}}に追加しました", - "xpack.timelines.hoverActions.columnToggleLabel": "表の{field}列を切り替える", "xpack.timelines.hoverActions.fieldLabel": "フィールド", "xpack.timelines.hoverActions.filterIn": "フィルタリング", "xpack.timelines.hoverActions.filterOut": "除外", - "xpack.timelines.hoverActions.moreActions": "さらにアクションを表示", - "xpack.timelines.hoverActions.nestedColumnToggleLabel": "{field}フィールドはオブジェクトであり、列として追加できるネストされたフィールドに分解されます", "xpack.timelines.hoverActions.tooltipWithKeyboardShortcut.pressTooltipLabel": "プレス", "xpack.timelines.updated": "更新しました", "xpack.timelines.updating": "更新中...", @@ -45435,7 +45431,6 @@ "xpack.upgradeAssistant.esDeprecationLogs.documentationLinkText": "ドキュメント", "xpack.upgradeAssistant.esDeprecationLogs.pageDescription": "廃止予定ログを確認し、アプリケーションが廃止予定のAPIを使用しているかどうかを判断してください。アップグレード後に、エラーまたは動作の変更を防止するには、アプリケーションを更新します。", "xpack.upgradeAssistant.esDeprecationLogs.pageTitle": "Elasticsearchの廃止予定ログ", - "xpack.upgradeAssistant.esDeprecations.batchReindexingDocsDescription": "1つの要求で複数の再インデックスタスクを開始するには、Kibana {docsLink}を使用します。", "xpack.upgradeAssistant.esDeprecations.batchReindexingDocsLink": "バッチ再インデックスAPI", "xpack.upgradeAssistant.esDeprecations.clusterDeprecationTypeLabel": "クラスター", "xpack.upgradeAssistant.esDeprecations.clusterSettings.deleteCompleteText": "廃止予定の設定が削除されました", diff --git a/x-pack/platform/plugins/private/translations/translations/zh-CN.json b/x-pack/platform/plugins/private/translations/translations/zh-CN.json index 445b20941fc97..6e9f8c361642e 100644 --- a/x-pack/platform/plugins/private/translations/translations/zh-CN.json +++ b/x-pack/platform/plugins/private/translations/translations/zh-CN.json @@ -30511,7 +30511,6 @@ "xpack.observability.customThreshold.rule.reason.forTheLast": "持续时间:{duration}", "xpack.observability.customThreshold.rule.reason.group": "组:{group}", "xpack.observability.customThreshold.rule.reasonActionVariableDescription": "告警原因的简洁描述", - "xpack.observability.customThreshold.rule.schema.invalidFilterQuery": "filterQuery 必须是有效的 KQL 筛选(错误:{errorMessage})", "xpack.observability.customThreshold.rule.sourceConfiguration.missingHttp": "无法加载源:无 HTTP 客户端可用。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureBody": "无法对指标配置应用更改。请稍后重试。", "xpack.observability.customThreshold.rule.sourceConfiguration.updateFailureTitle": "配置更新失败", @@ -43418,12 +43417,9 @@ "xpack.timelines.dragAndDrop.copyToClipboardTooltip": "复制到剪贴板", "xpack.timelines.hoverActions.addToTimeline": "添加到时间线调查", "xpack.timelines.hoverActions.addToTimeline.addedFieldMessage": "已将 {fieldOrValue} 添加到{isTimeline, select, true {时间线} other {模板}}", - "xpack.timelines.hoverActions.columnToggleLabel": "在表中切换 {field} 列", "xpack.timelines.hoverActions.fieldLabel": "字段", "xpack.timelines.hoverActions.filterIn": "筛选", "xpack.timelines.hoverActions.filterOut": "筛除", - "xpack.timelines.hoverActions.moreActions": "更多操作", - "xpack.timelines.hoverActions.nestedColumnToggleLabel": "{field} 字段是对象,并分解为可以添加为列的嵌套字段", "xpack.timelines.hoverActions.tooltipWithKeyboardShortcut.pressTooltipLabel": "按", "xpack.timelines.updated": "已更新", "xpack.timelines.updating": "正在更新......", @@ -44760,7 +44756,6 @@ "xpack.upgradeAssistant.esDeprecationLogs.documentationLinkText": "文档", "xpack.upgradeAssistant.esDeprecationLogs.pageDescription": "请复查弃用日志以确定您的应用程序是否正使用任何已弃用的 API。在升级后,请更新应用程序以防止错误或行为更改。", "xpack.upgradeAssistant.esDeprecationLogs.pageTitle": "Elasticsearch 弃用日志", - "xpack.upgradeAssistant.esDeprecations.batchReindexingDocsDescription": "要在单一请求中启动多个重新索引任务,请使用 Kibana {docsLink}。", "xpack.upgradeAssistant.esDeprecations.batchReindexingDocsLink": "批量重新索引 API", "xpack.upgradeAssistant.esDeprecations.clusterDeprecationTypeLabel": "集群", "xpack.upgradeAssistant.esDeprecations.clusterSettings.deleteCompleteText": "过时设置已移除", diff --git a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx index cec257f195892..0a25792cad340 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx +++ b/x-pack/platform/plugins/private/upgrade_assistant/public/application/components/es_deprecations/es_deprecations.tsx @@ -77,7 +77,7 @@ const getBatchReindexLink = (docLinks: DocLinksStart) => { return ( [ { title: 'mock-deprecation-title', correctiveActions: { @@ -31,7 +31,7 @@ const mockKibanaDeprecations: DomainDeprecationDetails[] = [ describe('getKibanaUpgradeStatus', () => { const deprecationsClient = deprecationsServiceMock.createClient(); - deprecationsClient.getAllDeprecations.mockResolvedValue(mockKibanaDeprecations); + deprecationsClient.getAllDeprecations.mockResolvedValue(mockKibanaDeprecations()); it('returns the correct shape of data', async () => { const resp = await getKibanaUpgradeStatus(deprecationsClient); @@ -39,7 +39,7 @@ describe('getKibanaUpgradeStatus', () => { }); it('returns totalCriticalDeprecations > 0 when critical issues found', async () => { - deprecationsClient.getAllDeprecations.mockResolvedValue(mockKibanaDeprecations); + deprecationsClient.getAllDeprecations.mockResolvedValue(mockKibanaDeprecations()); await expect(getKibanaUpgradeStatus(deprecationsClient)).resolves.toHaveProperty( 'totalCriticalDeprecations', @@ -55,4 +55,98 @@ describe('getKibanaUpgradeStatus', () => { 0 ); }); + + it('returns totalCriticalDeprecations > 0, but ignores API deprecations', async () => { + deprecationsClient.getAllDeprecations.mockResolvedValue([ + ...mockKibanaDeprecations(), + ...mockKibanaDeprecations(), + { + title: 'mock-deprecation-title', + correctiveActions: { + manualSteps: [], + }, + apiId: 'foo', + deprecationType: 'api', + documentationUrl: 'testDocUrl', + level: 'warning', + message: 'testMessage', + domainId: 'security', + }, + { + title: 'mock-deprecation-title', + correctiveActions: { + manualSteps: [], + }, + apiId: 'foo', + deprecationType: 'api', + documentationUrl: 'testDocUrl', + level: 'critical', + message: 'testMessage', + domainId: 'security', + }, + { + title: 'mock-deprecation-title', + correctiveActions: { + manualSteps: [], + }, + apiId: 'foo', + deprecationType: 'api', + documentationUrl: 'testDocUrl', + level: 'critical', + message: 'testMessage', + domainId: 'security', + }, + ]); + + await expect(getKibanaUpgradeStatus(deprecationsClient)).resolves.toHaveProperty( + 'totalCriticalDeprecations', + 2 + ); + }); + + it('returns totalCriticalDeprecations === 0 when only critical API deprecations', async () => { + deprecationsClient.getAllDeprecations.mockResolvedValue([ + { + title: 'mock-deprecation-title', + correctiveActions: { + manualSteps: [], + }, + apiId: 'foo', + deprecationType: 'api', + documentationUrl: 'testDocUrl', + level: 'warning', + message: 'testMessage', + domainId: 'security', + }, + { + title: 'mock-deprecation-title', + correctiveActions: { + manualSteps: [], + }, + apiId: 'foo', + deprecationType: 'api', + documentationUrl: 'testDocUrl', + level: 'critical', + message: 'testMessage', + domainId: 'security', + }, + { + title: 'mock-deprecation-title', + correctiveActions: { + manualSteps: [], + }, + apiId: 'foo', + deprecationType: 'api', + documentationUrl: 'testDocUrl', + level: 'critical', + message: 'testMessage', + domainId: 'security', + }, + ]); + + await expect(getKibanaUpgradeStatus(deprecationsClient)).resolves.toHaveProperty( + 'totalCriticalDeprecations', + 0 + ); + }); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/server/lib/kibana_status.ts b/x-pack/platform/plugins/private/upgrade_assistant/server/lib/kibana_status.ts index dd07e2761b5cc..3b8b34fd573bd 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/server/lib/kibana_status.ts +++ b/x-pack/platform/plugins/private/upgrade_assistant/server/lib/kibana_status.ts @@ -11,7 +11,9 @@ export const getKibanaUpgradeStatus = async (deprecationsClient: DeprecationsCli const kibanaDeprecations: DomainDeprecationDetails[] = await deprecationsClient.getAllDeprecations(); - const totalCriticalDeprecations = kibanaDeprecations.filter((d) => d.level === 'critical').length; + const totalCriticalDeprecations = kibanaDeprecations.filter( + (d) => d.deprecationType !== 'api' && d.level === 'critical' + ).length; return { totalCriticalDeprecations, diff --git a/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts b/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts index 5fdd333347f57..7726dceffb7d5 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts +++ b/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts @@ -799,18 +799,42 @@ describe('reindexService', () => { expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); expect(clusterClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ actions: [ - { add: { index: 'myIndex-reindex-0', alias: 'myIndex' } }, + { add: { index: 'myIndex-reindex-0', alias: 'myIndex', is_hidden: false } }, { remove_index: { index: 'myIndex' } }, ], }); }); + it.each([true, 'true'])( + 'switches aliases, passing on hidden status of index to alias if defined (hidden value: %p)', + async (hidden) => { + clusterClient.asCurrentUser.indices.getAlias.mockResponseOnce({ + myIndex: { aliases: {} }, + }); + clusterClient.asCurrentUser.indices.getSettings.mockResponseOnce({ + myIndex: { settings: { index: { hidden } } }, + }); + clusterClient.asCurrentUser.indices.updateAliases.mockResponseOnce({ + acknowledged: true, + }); + const updatedOp = await service.processNextStep(reindexOp); + expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); + expect(clusterClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ + actions: [ + { add: { index: 'myIndex-reindex-0', alias: 'myIndex', is_hidden: true } }, + { remove_index: { index: 'myIndex' } }, + ], + }); + } + ); + it('moves existing aliases over to new index', async () => { clusterClient.asCurrentUser.indices.getAlias.mockResponseOnce({ myIndex: { aliases: { myAlias: {}, myFilteredAlias: { filter: { term: { https: true } } }, + myHiddenAlias: { is_hidden: true }, }, }, }); @@ -821,7 +845,7 @@ describe('reindexService', () => { expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); expect(clusterClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ actions: [ - { add: { index: 'myIndex-reindex-0', alias: 'myIndex' } }, + { add: { index: 'myIndex-reindex-0', alias: 'myIndex', is_hidden: false } }, { remove_index: { index: 'myIndex' } }, { add: { index: 'myIndex-reindex-0', alias: 'myAlias' } }, { @@ -831,6 +855,13 @@ describe('reindexService', () => { filter: { term: { https: true } }, }, }, + { + add: { + index: 'myIndex-reindex-0', + alias: 'myHiddenAlias', + is_hidden: true, + }, + }, ], }); }); diff --git a/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.ts b/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.ts index a17dbdadf0214..31bfe99f34c75 100644 --- a/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.ts +++ b/x-pack/platform/plugins/private/upgrade_assistant/server/lib/reindexing/reindex_service.ts @@ -349,6 +349,12 @@ export const reindexServiceFactory = ( return response[indexName]?.aliases ?? {}; }; + const isIndexHidden = async (indexName: string) => { + const response = await esClient.indices.getSettings({ index: indexName }); + const isHidden = response[indexName]?.settings?.index?.hidden; + return isHidden === true || isHidden === 'true'; + }; + /** * Restores the original index settings in the new index that had other defaults for reindexing performance reasons * @param reindexOp @@ -391,9 +397,11 @@ export const reindexServiceFactory = ( add: { index: newIndexName, alias: aliasName, ...existingAliases[aliasName] }, })); + const isHidden = await isIndexHidden(indexName); + const aliasResponse = await esClient.indices.updateAliases({ actions: [ - { add: { index: newIndexName, alias: indexName } }, + { add: { index: newIndexName, alias: indexName, is_hidden: isHidden } }, { remove_index: { index: indexName } }, ...extraAliases, ], diff --git a/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap b/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap index b6abe08b9bd9b..cd1e050fd4ab8 100644 --- a/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap +++ b/x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap @@ -9540,30 +9540,73 @@ Object { ], "type": "string", }, - "message": Object { + "prompts": Object { "flags": Object { "error": [Function], }, - "metas": Array [ - Object { - "x-oas-min-length": 1, - }, - ], - "rules": Array [ + "items": Array [ Object { - "args": Object { - "method": [Function], + "flags": Object { + "default": Object { + "special": "deep", + }, + "error": [Function], + "presence": "optional", }, - "name": "custom", - }, - Object { - "args": Object { - "method": [Function], + "keys": Object { + "message": Object { + "flags": Object { + "error": [Function], + }, + "metas": Array [ + Object { + "x-oas-min-length": 1, + }, + ], + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + "statuses": Object { + "flags": Object { + "error": [Function], + }, + "items": Array [ + Object { + "flags": Object { + "error": [Function], + "presence": "optional", + }, + "rules": Array [ + Object { + "args": Object { + "method": [Function], + }, + "name": "custom", + }, + ], + "type": "string", + }, + ], + "type": "array", + }, }, - "name": "custom", + "type": "object", }, ], - "type": "string", + "type": "array", }, "rule": Object { "flags": Object { diff --git a/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.test.ts b/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.test.ts index 26c37b36566e4..ba5a184cae8a0 100644 --- a/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.test.ts +++ b/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.test.ts @@ -6,7 +6,9 @@ */ import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging-mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { errors } from '@elastic/elasticsearch'; import { getCounts, getExecutionsPerDayCount, @@ -14,9 +16,13 @@ import { getTotalCount, } from './actions_telemetry'; -const mockLogger = loggingSystemMock.create().get(); +let logger: MockedLogger; describe('actions telemetry', () => { + beforeEach(() => { + logger = loggerMock.create(); + }); + test('getTotalCount should replace first symbol . to __ for action types names', async () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; mockEsClient.search.mockResponse( @@ -107,7 +113,7 @@ describe('actions telemetry', () => { }, } ); - const telemetry = await getTotalCount(mockEsClient, 'test', mockLogger); + const telemetry = await getTotalCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(1); @@ -126,16 +132,24 @@ describe('actions telemetry', () => { `); }); - test('getTotalCount should return empty results if query throws error', async () => { + test('getTotalCount should return empty results and log warning if query throws error', async () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; mockEsClient.search.mockRejectedValue(new Error('oh no')); - const telemetry = await getTotalCount(mockEsClient, 'test', mockLogger); + const telemetry = await getTotalCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(1); - expect(mockLogger.warn).toHaveBeenCalledWith( - `Error executing actions telemetry task: getTotalCount - {}` + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(0); + expect(loggerCalls.warn).toHaveLength(1); + expect(loggerCalls.warn[0][0]).toEqual( + `Error executing actions telemetry task: getTotalCount - Error: oh no` ); + // logger meta + expect(loggerCalls.warn[0][1]?.tags).toEqual(['actions', 'telemetry-failed']); + expect(loggerCalls.warn[0][1]?.error?.stack_trace).toBeDefined(); + expect(telemetry).toMatchInlineSnapshot(` Object { "countByType": Object {}, @@ -147,6 +161,60 @@ describe('actions telemetry', () => { `); }); + test('getTotalCount should return empty results and log debug if query throws search_phase_execution_exception error', async () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getTotalCount(mockEsClient, 'test', logger); + + expect(mockEsClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(1); + expect(loggerCalls.debug[0][0]).toMatchInlineSnapshot(` + "Error executing actions telemetry task: getTotalCount - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[0][1]?.tags).toEqual(['actions', 'telemetry-failed']); + expect(loggerCalls.debug[0][1]?.error?.stack_trace).toBeDefined(); + + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toMatchInlineSnapshot(` + Object { + "countByType": Object {}, + "countGenAiProviderTypes": Object {}, + "countTotal": 0, + "errorMessage": "no_shard_available_action_exception", + "hasErrors": true, + } + `); + }); + test('getInUseTotalCount', async () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; mockEsClient.search.mockResponseOnce( @@ -202,7 +270,7 @@ describe('actions telemetry', () => { ], }, }); - const telemetry = await getInUseTotalCount(mockEsClient, 'test', mockLogger); + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(2); expect(telemetry).toMatchInlineSnapshot(` @@ -287,7 +355,7 @@ describe('actions telemetry', () => { ], }, }); - const telemetry = await getInUseTotalCount(mockEsClient, 'test', mockLogger, undefined, [ + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger, undefined, [ { id: 'test', actionTypeId: '.email', @@ -332,16 +400,23 @@ describe('actions telemetry', () => { `); }); - test('getInUseTotalCount should return empty results if query throws error', async () => { + test('getInUseTotalCount should return empty results and log warning if query throws error', async () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; mockEsClient.search.mockRejectedValue(new Error('oh no')); - const telemetry = await getInUseTotalCount(mockEsClient, 'test', mockLogger); + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(1); - expect(mockLogger.warn).toHaveBeenCalledWith( - `Error executing actions telemetry task: getInUseTotalCount - {}` + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(0); + expect(loggerCalls.warn).toHaveLength(1); + expect(loggerCalls.warn[0][0]).toEqual( + `Error executing actions telemetry task: getInUseTotalCount - Error: oh no` ); + // logger meta + expect(loggerCalls.warn[0][1]?.tags).toEqual(['actions', 'telemetry-failed']); + expect(loggerCalls.warn[0][1]?.error?.stack_trace).toBeDefined(); + expect(telemetry).toMatchInlineSnapshot(` Object { "countByAlertHistoryConnectorType": 0, @@ -355,6 +430,62 @@ describe('actions telemetry', () => { `); }); + test('getInUseTotalCount should return empty results and log debug if query throws search_phase_execution_exception error', async () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger); + + expect(mockEsClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(1); + expect(loggerCalls.debug[0][0]).toMatchInlineSnapshot(` + "Error executing actions telemetry task: getInUseTotalCount - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[0][1]?.tags).toEqual(['actions', 'telemetry-failed']); + expect(loggerCalls.debug[0][1]?.error?.stack_trace).toBeDefined(); + + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toMatchInlineSnapshot(` + Object { + "countByAlertHistoryConnectorType": 0, + "countByType": Object {}, + "countEmailByService": Object {}, + "countNamespaces": 0, + "countTotal": 0, + "errorMessage": "no_shard_available_action_exception", + "hasErrors": true, + } + `); + }); + test('getTotalCount accounts for preconfigured connectors', async () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; mockEsClient.search.mockResponse( @@ -445,7 +576,7 @@ describe('actions telemetry', () => { }, } ); - const telemetry = await getTotalCount(mockEsClient, 'test', mockLogger, [ + const telemetry = await getTotalCount(mockEsClient, 'test', logger, [ { id: 'test', actionTypeId: '.test', @@ -501,7 +632,7 @@ describe('actions telemetry', () => { }, } ); - const telemetry = await getTotalCount(mockEsClient, 'test', mockLogger, [ + const telemetry = await getTotalCount(mockEsClient, 'test', logger, [ { id: 'system_action:system-connector-test.system-action', actionTypeId: 'test.system-action', @@ -615,7 +746,7 @@ describe('actions telemetry', () => { ], }, }); - const telemetry = await getInUseTotalCount(mockEsClient, 'test', mockLogger, undefined, [ + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger, undefined, [ { id: 'anotherServerLog', actionTypeId: '.server-log', @@ -721,7 +852,7 @@ describe('actions telemetry', () => { }, }); - const telemetry = await getInUseTotalCount(mockEsClient, 'test', mockLogger, undefined, []); + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger, undefined, []); expect(mockEsClient.search).toHaveBeenCalledTimes(2); expect(telemetry).toMatchInlineSnapshot(` @@ -827,7 +958,7 @@ describe('actions telemetry', () => { ], }, }); - const telemetry = await getInUseTotalCount(mockEsClient, 'test', mockLogger); + const telemetry = await getInUseTotalCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(2); expect(telemetry).toMatchInlineSnapshot(` @@ -961,7 +1092,7 @@ describe('actions telemetry', () => { }, } ); - const telemetry = await getExecutionsPerDayCount(mockEsClient, 'test', mockLogger); + const telemetry = await getExecutionsPerDayCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(1); expect(telemetry).toStrictEqual({ @@ -995,16 +1126,24 @@ describe('actions telemetry', () => { }); }); - test('getExecutionsPerDayCount should return empty results if query throws error', async () => { + test('getExecutionsPerDayCount should return empty results and log warning if query throws error', async () => { const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; mockEsClient.search.mockRejectedValue(new Error('oh no')); - const telemetry = await getExecutionsPerDayCount(mockEsClient, 'test', mockLogger); + const telemetry = await getExecutionsPerDayCount(mockEsClient, 'test', logger); expect(mockEsClient.search).toHaveBeenCalledTimes(1); - expect(mockLogger.warn).toHaveBeenCalledWith( - `Error executing actions telemetry task: getExecutionsPerDayCount - {}` + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(0); + expect(loggerCalls.warn).toHaveLength(1); + expect(loggerCalls.warn[0][0]).toEqual( + `Error executing actions telemetry task: getExecutionsPerDayCount - Error: oh no` ); + // logger meta + expect(loggerCalls.warn[0][1]?.tags).toEqual(['actions', 'telemetry-failed']); + expect(loggerCalls.warn[0][1]?.error?.stack_trace).toBeDefined(); + expect(telemetry).toMatchInlineSnapshot(` Object { "avgExecutionTime": 0, @@ -1020,6 +1159,64 @@ describe('actions telemetry', () => { `); }); + test('getExecutionsPerDayCount should return empty results and log debug if query throws search_phase_execution_exception error', async () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getExecutionsPerDayCount(mockEsClient, 'test', logger); + + expect(mockEsClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(1); + expect(loggerCalls.debug[0][0]).toMatchInlineSnapshot(` + "Error executing actions telemetry task: getExecutionsPerDayCount - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[0][1]?.tags).toEqual(['actions', 'telemetry-failed']); + expect(loggerCalls.debug[0][1]?.error?.stack_trace).toBeDefined(); + + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toMatchInlineSnapshot(` + Object { + "avgExecutionTime": 0, + "avgExecutionTimeByType": Object {}, + "countByType": Object {}, + "countFailed": 0, + "countFailedByType": Object {}, + "countRunOutcomeByConnectorType": Object {}, + "countTotal": 0, + "errorMessage": "no_shard_available_action_exception", + "hasErrors": true, + } + `); + }); + it('getCounts', () => { const aggs = { '.d3security': 2, diff --git a/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.ts b/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.ts index b7e93f5157140..d0964266ea36c 100644 --- a/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.ts +++ b/x-pack/platform/plugins/shared/actions/server/usage/actions_telemetry.ts @@ -20,6 +20,7 @@ import { getActionExecutions, getActionsCount, } from './lib/actions_telemetry_util'; +import { parseAndLogError } from './lib/parse_and_log_error'; export interface InMemoryAggRes { total: number; @@ -108,9 +109,7 @@ export async function getTotalCount( countGenAiProviderTypes, }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - - logger.warn(`Error executing actions telemetry task: getTotalCount - ${JSON.stringify(err)}`); + const errorMessage = parseAndLogError(err, `getTotalCount`, logger); return { hasErrors: true, @@ -387,11 +386,8 @@ export async function getInUseTotalCount( countNamespaces: namespacesList.size, }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); + const errorMessage = parseAndLogError(err, `getInUseTotalCount`, logger); - logger.warn( - `Error executing actions telemetry task: getInUseTotalCount - ${JSON.stringify(err)}` - ); return { hasErrors: true, errorMessage, @@ -657,10 +653,8 @@ export async function getExecutionsPerDayCount( ), }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - logger.warn( - `Error executing actions telemetry task: getExecutionsPerDayCount - ${JSON.stringify(err)}` - ); + const errorMessage = parseAndLogError(err, `getExecutionsPerDayCount`, logger); + return { hasErrors: true, errorMessage, diff --git a/x-pack/platform/plugins/shared/actions/server/usage/lib/parse_and_log_error.ts b/x-pack/platform/plugins/shared/actions/server/usage/lib/parse_and_log_error.ts new file mode 100644 index 0000000000000..d6e3a93887675 --- /dev/null +++ b/x-pack/platform/plugins/shared/actions/server/usage/lib/parse_and_log_error.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/core/server'; + +export function parseAndLogError(err: Error, errType: string, logger: Logger): string { + const errorMessage = err && err.message ? err.message : err.toString(); + let returnedErrorMessage = errorMessage; + + const errorStr = JSON.stringify(err); + const logMessage = `Error executing actions telemetry task: ${errType} - ${err}`; + const logOptions = { + tags: ['actions', 'telemetry-failed'], + error: { stack_trace: err.stack }, + }; + + // If error string contains "no_shard_available_action_exception", debug log it + if (errorStr.includes('no_shard_available_action_exception')) { + // the no_shard_available_action_exception can be wordy and the error message returned from this function + // gets stored in the task state so lets simplify + returnedErrorMessage = 'no_shard_available_action_exception'; + if (logger.isLevelEnabled('debug')) { + logger.debug(logMessage, logOptions); + } + } else { + logger.warn(logMessage, logOptions); + } + + return returnedErrorMessage; +} diff --git a/x-pack/platform/plugins/shared/ai_infra/product_doc_base/server/routes/installation.ts b/x-pack/platform/plugins/shared/ai_infra/product_doc_base/server/routes/installation.ts index 1e6b5545ebb4e..e10e74b1ffb07 100644 --- a/x-pack/platform/plugins/shared/ai_infra/product_doc_base/server/routes/installation.ts +++ b/x-pack/platform/plugins/shared/ai_infra/product_doc_base/server/routes/installation.ts @@ -6,6 +6,7 @@ */ import type { IRouter } from '@kbn/core/server'; +import { ApiPrivileges } from '@kbn/security-authorization-core-common'; import { INSTALLATION_STATUS_API_PATH, INSTALL_ALL_API_PATH, @@ -32,7 +33,7 @@ export const registerInstallationRoutes = ({ }, security: { authz: { - requiredPrivileges: ['manage_llm_product_doc'], + requiredPrivileges: [ApiPrivileges.manage('llm_product_doc')], }, }, }, @@ -60,7 +61,7 @@ export const registerInstallationRoutes = ({ }, security: { authz: { - requiredPrivileges: ['manage_llm_product_doc'], + requiredPrivileges: [ApiPrivileges.manage('llm_product_doc')], }, }, }, @@ -93,7 +94,7 @@ export const registerInstallationRoutes = ({ }, security: { authz: { - requiredPrivileges: ['manage_llm_product_doc'], + requiredPrivileges: [ApiPrivileges.manage('llm_product_doc')], }, }, }, diff --git a/x-pack/platform/plugins/shared/ai_infra/product_doc_base/tsconfig.json b/x-pack/platform/plugins/shared/ai_infra/product_doc_base/tsconfig.json index 66b31885ea68b..bb592405ba8da 100644 --- a/x-pack/platform/plugins/shared/ai_infra/product_doc_base/tsconfig.json +++ b/x-pack/platform/plugins/shared/ai_infra/product_doc_base/tsconfig.json @@ -26,5 +26,6 @@ "@kbn/licensing-plugin", "@kbn/task-manager-plugin", "@kbn/inference-common", + "@kbn/security-authorization-core-common", ] } diff --git a/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client.ts b/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client.ts index d62f579e4566e..baf786177ba01 100644 --- a/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client.ts @@ -376,6 +376,8 @@ export class AlertsClient< throw new Error(`Must specify either execution UUID or time range for AAD alert query.`); } + const maxAlertLimit = this.legacyAlertsClient.getMaxAlertLimit(); + const getQueryParams = { executionUuid, start, @@ -383,6 +385,7 @@ export class AlertsClient< ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }; const formatAlert = this.ruleType.alerts?.formatAlert; @@ -641,12 +644,14 @@ export class AlertsClient< ); } const isLifecycleAlert = this.ruleType.autoRecoverAlerts ?? false; + const maxAlertLimit = this.legacyAlertsClient.getMaxAlertLimit(); const query = getMaintenanceWindowAlertsQuery({ executionUuid, ruleId, maintenanceWindows, action: isLifecycleAlert ? 'open' : undefined, + maxAlertLimit, }); const response = await this.search(query); diff --git a/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client_fixtures.ts b/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client_fixtures.ts index 0da20a5e49b70..db666e5501975 100644 --- a/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client_fixtures.ts +++ b/x-pack/platform/plugins/shared/alerting/server/alerts_client/alerts_client_fixtures.ts @@ -219,7 +219,7 @@ export const getExpectedQueryByExecutionUuid = ({ ], }, }, - size: 100, + size: 1000, track_total_hits: true, }, ignore_unavailable: true, @@ -382,7 +382,7 @@ export const getExpectedQueryByTimeRange = ({ filter, }, }, - size: 100, + size: 1000, track_total_hits: true, }, ignore_unavailable: true, diff --git a/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.test.ts b/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.test.ts index db341d35ce971..88a77372099b9 100644 --- a/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.test.ts +++ b/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.test.ts @@ -243,6 +243,21 @@ describe('Legacy Alerts Client', () => { expect(mockCreateAlertFactory.hasReachedAlertLimit).toHaveBeenCalled(); }); + test('getMaxAlertLimit() should return the maxAlertLimit', async () => { + const alertsClient = new LegacyAlertsClient({ + alertingEventLogger, + logger, + request: fakeRequest, + spaceId: 'space1', + ruleType, + maintenanceWindowsService, + }); + + await alertsClient.initializeExecution(defaultExecutionOpts); + + expect(alertsClient.getMaxAlertLimit()).toBe(1000); + }); + test('processAlerts() should call processAlerts, trimRecoveredAlerts and getAlertsForNotifications', async () => { maintenanceWindowsService.getMaintenanceWindows.mockReturnValue({ maintenanceWindows: [ diff --git a/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.ts b/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.ts index eb77de7d1918a..b21366da6382d 100644 --- a/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.ts +++ b/x-pack/platform/plugins/shared/alerting/server/alerts_client/legacy_alerts_client.ts @@ -260,6 +260,10 @@ export class LegacyAlertsClient< return this.alertFactory!.hasReachedAlertLimit(); } + public getMaxAlertLimit(): number { + return this.maxAlerts; + } + public checkLimitUsage() { return this.alertFactory!.alertLimit.checkLimitUsage(); } diff --git a/x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts b/x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts index ab3edece0becc..75d6780880beb 100644 --- a/x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts +++ b/x-pack/platform/plugins/shared/alerting/server/alerts_client/lib/get_summarized_alerts_query.ts @@ -41,7 +41,6 @@ import { FormatAlert } from '../../types'; import { expandFlattenedAlert } from './format_alert'; import { injectAnalyzeWildcard } from './inject_analyze_wildcard'; -const MAX_ALERT_DOCS_TO_RETURN = 100; enum AlertTypes { NEW = 0, ONGOING, @@ -53,6 +52,7 @@ const getLifecycleAlertsQueryByExecutionUuid = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }: GetLifecycleAlertsQueryByExecutionUuidParams): Array => { // lifecycle alerts assign a different action to an alert depending // on whether it is new/ongoing/recovered. query for each action in order @@ -65,6 +65,7 @@ const getLifecycleAlertsQueryByExecutionUuid = ({ excludedAlertInstanceIds, action: 'open', alertsFilter, + maxAlertLimit, }), getQueryByExecutionUuid({ executionUuid, @@ -72,6 +73,7 @@ const getLifecycleAlertsQueryByExecutionUuid = ({ excludedAlertInstanceIds, action: 'active', alertsFilter, + maxAlertLimit, }), getQueryByExecutionUuid({ executionUuid, @@ -79,6 +81,7 @@ const getLifecycleAlertsQueryByExecutionUuid = ({ excludedAlertInstanceIds, action: 'close', alertsFilter, + maxAlertLimit, }), ]; }; @@ -89,6 +92,7 @@ const getLifecycleAlertsQueryByTimeRange = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }: GetLifecycleAlertsQueryByTimeRangeParams): Array => { return [ getQueryByTimeRange({ @@ -98,6 +102,7 @@ const getLifecycleAlertsQueryByTimeRange = ({ excludedAlertInstanceIds, type: AlertTypes.NEW, alertsFilter, + maxAlertLimit, }), getQueryByTimeRange({ start, @@ -106,6 +111,7 @@ const getLifecycleAlertsQueryByTimeRange = ({ excludedAlertInstanceIds, type: AlertTypes.ONGOING, alertsFilter, + maxAlertLimit, }), getQueryByTimeRange({ start, @@ -114,6 +120,7 @@ const getLifecycleAlertsQueryByTimeRange = ({ excludedAlertInstanceIds, type: AlertTypes.RECOVERED, alertsFilter, + maxAlertLimit, }), ]; }; @@ -124,6 +131,7 @@ const getQueryByExecutionUuid = ({ excludedAlertInstanceIds, action, alertsFilter, + maxAlertLimit, }: GetQueryByExecutionUuidParams): SearchRequest['body'] => { const filter: QueryDslQueryContainer[] = [ { @@ -170,7 +178,7 @@ const getQueryByExecutionUuid = ({ } return { - size: MAX_ALERT_DOCS_TO_RETURN, + size: maxAlertLimit, track_total_hits: true, query: { bool: { @@ -187,6 +195,7 @@ const getQueryByTimeRange = ({ excludedAlertInstanceIds, type, alertsFilter, + maxAlertLimit, }: GetQueryByTimeRangeParams): SearchRequest['body'] => { // base query filters the alert documents for a rule by the given time range let filter: QueryDslQueryContainer[] = [ @@ -267,7 +276,7 @@ const getQueryByTimeRange = ({ } return { - size: MAX_ALERT_DOCS_TO_RETURN, + size: maxAlertLimit, track_total_hits: true, query: { bool: { @@ -282,6 +291,7 @@ export const getQueryByScopedQueries = ({ ruleId, action, maintenanceWindows, + maxAlertLimit, }: GetQueryByScopedQueriesParams): SearchRequest['body'] => { const filters: QueryDslQueryContainer[] = [ { @@ -330,7 +340,7 @@ export const getQueryByScopedQueries = ({ aggs: { alertId: { top_hits: { - size: MAX_ALERT_DOCS_TO_RETURN, + size: maxAlertLimit, _source: { includes: [ALERT_UUID], }, @@ -460,6 +470,7 @@ const getLifecycleAlertsQueries = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }: GetAlertsQueryParams): Array => { let queryBodies; if (!!executionUuid) { @@ -468,6 +479,7 @@ const getLifecycleAlertsQueries = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }); } else { queryBodies = getLifecycleAlertsQueryByTimeRange({ @@ -476,6 +488,7 @@ const getLifecycleAlertsQueries = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }); } @@ -489,6 +502,7 @@ const getContinualAlertsQuery = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }: GetAlertsQueryParams): SearchRequest['body'] => { let queryBody; if (!!executionUuid) { @@ -497,6 +511,7 @@ const getContinualAlertsQuery = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }); } else { queryBody = getQueryByTimeRange({ @@ -505,6 +520,7 @@ const getContinualAlertsQuery = ({ ruleId, excludedAlertInstanceIds, alertsFilter, + maxAlertLimit, }); } @@ -516,12 +532,14 @@ const getMaintenanceWindowAlertsQuery = ({ ruleId, action, maintenanceWindows, + maxAlertLimit, }: GetMaintenanceWindowAlertsQueryParams): SearchRequest['body'] => { return getQueryByScopedQueries({ executionUuid, ruleId, action, maintenanceWindows, + maxAlertLimit, }); }; diff --git a/x-pack/platform/plugins/shared/alerting/server/alerts_client/types.ts b/x-pack/platform/plugins/shared/alerting/server/alerts_client/types.ts index f3c4a85fa1b71..f7dd8bb1dff7a 100644 --- a/x-pack/platform/plugins/shared/alerting/server/alerts_client/types.ts +++ b/x-pack/platform/plugins/shared/alerting/server/alerts_client/types.ts @@ -221,12 +221,13 @@ export type UpdateAlertsMaintenanceWindowIdByScopedQueryParams = export type GetAlertsQueryParams = Omit< GetSummarizedAlertsParams, 'formatAlert' | 'isLifecycleAlert' | 'spaceId' ->; +> & { maxAlertLimit: number }; export interface GetLifecycleAlertsQueryByExecutionUuidParams { executionUuid: string; ruleId: string; excludedAlertInstanceIds: string[]; + maxAlertLimit: number; alertsFilter?: AlertsFilter | null; } @@ -239,6 +240,7 @@ export interface GetQueryByScopedQueriesParams { ruleId: string; executionUuid: string; maintenanceWindows: MaintenanceWindow[]; + maxAlertLimit: number; action?: string; } @@ -246,6 +248,7 @@ export interface GetMaintenanceWindowAlertsQueryParams { ruleId: string; maintenanceWindows: MaintenanceWindow[]; executionUuid: string; + maxAlertLimit: number; action?: string; } @@ -254,6 +257,7 @@ export interface GetLifecycleAlertsQueryByTimeRangeParams { end: Date; ruleId: string; excludedAlertInstanceIds: string[]; + maxAlertLimit: number; alertsFilter?: AlertsFilter | null; } diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.test.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.test.ts index e525657da8a9b..8ffd71b2ae829 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.test.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.test.ts @@ -6,64 +6,50 @@ */ import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging-mocks'; import { getTotalAlertsCountAggregations } from './get_telemetry_from_alerts'; +import { errors } from '@elastic/elasticsearch'; const elasticsearch = elasticsearchServiceMock.createStart(); const esClient = elasticsearch.client.asInternalUser; -const logger: ReturnType = loggingSystemMock.createLogger(); +let logger: MockedLogger; describe('kibana index telemetry', () => { beforeEach(() => { jest.resetAllMocks(); + logger = loggerMock.create(); }); - it('should return total alert couts and alert counts by rule type id', async () => { + it('should return total alert counts and alert counts by rule type id', async () => { esClient.search.mockResponseOnce({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 6, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 6, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { by_rule_type_id: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: '.index-threshold', - doc_count: 1, - }, - { - key: 'logs.alert.document.count', - doc_count: 2, - }, - { - key: 'document.test.', - doc_count: 3, - }, + { key: '.index-threshold', doc_count: 1 }, + { key: 'logs.alert.document.count', doc_count: 2 }, + { key: 'document.test.', doc_count: 3 }, ], }, }, }); - const telemetry = await getTotalAlertsCountAggregations({ - esClient, - logger, - }); + const telemetry = await getTotalAlertsCountAggregations({ esClient, logger }); expect(esClient.search).toHaveBeenCalledTimes(1); - expect(logger.debug).toHaveBeenCalledTimes(2); + const debugLogs = loggingSystemMock.collect(logger).debug; + expect(debugLogs).toHaveLength(2); + expect(debugLogs[0][0]).toEqual( + `query for getTotalAlertsCountAggregations - {\"index\":\".alerts-*\",\"size\":0,\"body\":{\"query\":{\"match_all\":{}},\"aggs\":{\"by_rule_type_id\":{\"terms\":{\"field\":\"kibana.alert.rule.rule_type_id\",\"size\":33}}}}}` + ); + expect(debugLogs[1][0]).toEqual( + `results for getTotalAlertsCountAggregations query - {\"took\":4,\"timed_out\":false,\"_shards\":{\"total\":1,\"successful\":1,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":6,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[]},\"aggregations\":{\"by_rule_type_id\":{\"doc_count_error_upper_bound\":0,\"sum_other_doc_count\":0,\"buckets\":[{\"key\":\".index-threshold\",\"doc_count\":1},{\"key\":\"logs.alert.document.count\",\"doc_count\":2},{\"key\":\"document.test.\",\"doc_count\":3}]}}}` + ); expect(telemetry).toEqual({ hasErrors: false, @@ -77,37 +63,18 @@ describe('kibana index telemetry', () => { }); }); - it('should return ', async () => { + it('should return on empty results', async () => { esClient.search.mockResponseOnce({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 0, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 0, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { - by_rule_type_id: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, + by_rule_type_id: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, }, }); - const telemetry = await getTotalAlertsCountAggregations({ - esClient, - logger, - }); + const telemetry = await getTotalAlertsCountAggregations({ esClient, logger }); expect(telemetry).toEqual({ hasErrors: false, @@ -116,17 +83,25 @@ describe('kibana index telemetry', () => { }); }); - test('should return empty results and log warning if query throws error', async () => { + it('should return empty results and log warning if query throws error', async () => { esClient.search.mockRejectedValueOnce(new Error('test')); - const telemetry = await getTotalAlertsCountAggregations({ - esClient, - logger, - }); + const telemetry = await getTotalAlertsCountAggregations({ esClient, logger }); expect(esClient.search).toHaveBeenCalledTimes(1); - expect(logger.debug).toHaveBeenCalledTimes(1); - expect(logger.warn).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(1); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getTotalAlertsCountAggregations - {\"index\":\".alerts-*\",\"size\":0,\"body\":{\"query\":{\"match_all\":{}},\"aggs\":{\"by_rule_type_id\":{\"terms\":{\"field\":\"kibana.alert.rule.rule_type_id\",\"size\":33}}}}}` + ); + expect(loggerCalls.warn).toHaveLength(1); + expect(loggerCalls.warn[0][0]).toEqual( + `Error executing alerting telemetry task: getTotalAlertsCountAggregations - Error: test` + ); + // logger meta + expect(loggerCalls.warn[0][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.warn[0][1]?.error?.stack_trace).toBeDefined(); expect(telemetry).toEqual({ hasErrors: true, @@ -135,4 +110,56 @@ describe('kibana index telemetry', () => { count_alerts_by_rule_type: {}, }); }); + + it('should return empty results and log debug log if query throws search_phase_execution_exception error', async () => { + esClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getTotalAlertsCountAggregations({ esClient, logger }); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(2); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getTotalAlertsCountAggregations - {\"index\":\".alerts-*\",\"size\":0,\"body\":{\"query\":{\"match_all\":{}},\"aggs\":{\"by_rule_type_id\":{\"terms\":{\"field\":\"kibana.alert.rule.rule_type_id\",\"size\":33}}}}}` + ); + expect(loggerCalls.debug[1][0]).toMatchInlineSnapshot(` + "Error executing alerting telemetry task: getTotalAlertsCountAggregations - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[1][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.debug[1][1]?.error?.stack_trace).toBeDefined(); + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toEqual({ + hasErrors: true, + errorMessage: `no_shard_available_action_exception`, + count_alerts_total: 0, + count_alerts_by_rule_type: {}, + }); + }); }); diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.ts index f5537687fd3dd..50acd17a9f9dd 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_alerts.ts @@ -14,6 +14,7 @@ import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { NUM_ALERTING_RULE_TYPES } from '../alerting_usage_collector'; import { parseSimpleRuleTypeBucket } from './parse_simple_rule_type_bucket'; import { AlertingUsage } from '../types'; +import { parseAndLogError } from './parse_and_log_error'; interface Opts { esClient: ElasticsearchClient; @@ -69,20 +70,10 @@ export async function getTotalAlertsCountAggregations({ return { hasErrors: false, count_alerts_total: totalAlertsCount ?? 0, - count_alerts_by_rule_type: parseSimpleRuleTypeBucket(aggregations.by_rule_type_id.buckets), + count_alerts_by_rule_type: parseSimpleRuleTypeBucket(aggregations?.by_rule_type_id?.buckets), }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - - logger.warn( - `Error executing alerting telemetry task: getTotalAlertsCountAggregations - ${JSON.stringify( - err - )}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err.stack }, - } - ); + const errorMessage = parseAndLogError(err, `getTotalAlertsCountAggregations`, logger); return { hasErrors: true, diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.test.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.test.ts index 64bc0ae8be0fb..1c8802fdb1726 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.test.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.test.ts @@ -6,6 +6,8 @@ */ import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging-mocks'; +import { errors } from '@elastic/elasticsearch'; import { getExecutionsPerDayCount, parseExecutionFailureByRuleType, @@ -17,11 +19,12 @@ import { const elasticsearch = elasticsearchServiceMock.createStart(); const esClient = elasticsearch.client.asInternalUser; -const logger: ReturnType = loggingSystemMock.createLogger(); +let logger: MockedLogger; describe('event log telemetry', () => { beforeEach(() => { jest.resetAllMocks(); + logger = loggerMock.create(); }); describe('parseRuleTypeBucket', () => { @@ -1106,20 +1109,8 @@ describe('event log telemetry', () => { esClient.search.mockResponse({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 148, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 148, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { by_rule_type_id: { doc_count_error_upper_bound: 0, @@ -1302,11 +1293,7 @@ describe('event log telemetry', () => { }, }); - const telemetry = await getExecutionsPerDayCount({ - esClient, - eventLogIndex: 'test', - logger, - }); + const telemetry = await getExecutionsPerDayCount({ esClient, eventLogIndex: 'test', logger }); expect(esClient.search).toHaveBeenCalledTimes(1); @@ -1416,17 +1403,14 @@ describe('event log telemetry', () => { test('should return empty results and log warning if query throws error', async () => { esClient.search.mockRejectedValue(new Error('oh no')); - const telemetry = await getExecutionsPerDayCount({ - esClient, - eventLogIndex: 'test', - logger, - }); + const telemetry = await getExecutionsPerDayCount({ esClient, eventLogIndex: 'test', logger }); expect(esClient.search).toHaveBeenCalledTimes(1); + const loggerCall = logger.warn.mock.calls[0][0]; const loggerMeta = logger.warn.mock.calls[0][1]; expect(loggerCall as string).toMatchInlineSnapshot( - `"Error executing alerting telemetry task: getExecutionsPerDayCount - {}"` + `"Error executing alerting telemetry task: getExecutionsPerDayCount - Error: oh no"` ); expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); expect(loggerMeta?.error?.stack_trace).toBeDefined(); @@ -1451,6 +1435,72 @@ describe('event log telemetry', () => { countRulesByExecutionStatus: {}, }); }); + + it('should return empty results and log debug log if query throws search_phase_execution_exception error', async () => { + esClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getExecutionsPerDayCount({ esClient, eventLogIndex: 'test', logger }); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(2); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getExecutionsPerDayCount - {\"index\":\"test\",\"size\":0,\"body\":{\"query\":{\"bool\":{\"filter\":{\"bool\":{\"must\":[{\"term\":{\"event.action\":\"execute\"}},{\"term\":{\"event.provider\":\"alerting\"}},{\"range\":{\"@timestamp\":{\"gte\":\"now-1d\"}}}]}}}},\"aggs\":{\"avg_execution_time\":{\"avg\":{\"field\":\"event.duration\"}},\"avg_es_search_duration\":{\"avg\":{\"field\":\"kibana.alert.rule.execution.metrics.es_search_duration_ms\"}},\"avg_total_search_duration\":{\"avg\":{\"field\":\"kibana.alert.rule.execution.metrics.total_search_duration_ms\"}},\"percentile_scheduled_actions\":{\"percentiles\":{\"field\":\"kibana.alert.rule.execution.metrics.number_of_generated_actions\",\"percents\":[50,90,99]}},\"percentile_alerts\":{\"percentiles\":{\"field\":\"kibana.alert.rule.execution.metrics.alert_counts.active\",\"percents\":[50,90,99]}},\"execution_failures\":{\"filter\":{\"term\":{\"event.outcome\":\"failure\"}},\"aggs\":{\"by_reason\":{\"terms\":{\"field\":\"event.reason\",\"size\":5}}}},\"by_rule_type_id\":{\"terms\":{\"field\":\"rule.category\",\"size\":33},\"aggs\":{\"avg_execution_time\":{\"avg\":{\"field\":\"event.duration\"}},\"avg_es_search_duration\":{\"avg\":{\"field\":\"kibana.alert.rule.execution.metrics.es_search_duration_ms\"}},\"avg_total_search_duration\":{\"avg\":{\"field\":\"kibana.alert.rule.execution.metrics.total_search_duration_ms\"}},\"percentile_scheduled_actions\":{\"percentiles\":{\"field\":\"kibana.alert.rule.execution.metrics.number_of_generated_actions\",\"percents\":[50,90,99]}},\"percentile_alerts\":{\"percentiles\":{\"field\":\"kibana.alert.rule.execution.metrics.alert_counts.active\",\"percents\":[50,90,99]}},\"execution_failures\":{\"filter\":{\"term\":{\"event.outcome\":\"failure\"}},\"aggs\":{\"by_reason\":{\"terms\":{\"field\":\"event.reason\",\"size\":5}}}}}},\"by_execution_status\":{\"terms\":{\"field\":\"event.outcome\"}}}}}` + ); + expect(loggerCalls.debug[1][0]).toMatchInlineSnapshot(` + "Error executing alerting telemetry task: getExecutionsPerDayCount - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[1][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.debug[1][1]?.error?.stack_trace).toBeDefined(); + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toStrictEqual({ + hasErrors: true, + errorMessage: 'no_shard_available_action_exception', + countTotalRuleExecutions: 0, + countRuleExecutionsByType: {}, + countTotalFailedExecutions: 0, + countFailedExecutionsByReason: {}, + countFailedExecutionsByReasonByType: {}, + avgExecutionTime: 0, + avgExecutionTimeByType: {}, + avgEsSearchDuration: 0, + avgEsSearchDurationByType: {}, + avgTotalSearchDuration: 0, + avgTotalSearchDurationByType: {}, + generatedActionsPercentiles: {}, + generatedActionsPercentilesByType: {}, + alertsPercentiles: {}, + alertsPercentilesByType: {}, + countRulesByExecutionStatus: {}, + }); + }); }); describe('getExecutionTimeoutsPerDayCount', () => { @@ -1458,37 +1508,16 @@ describe('event log telemetry', () => { esClient.search.mockResponse({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 4, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 4, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { by_rule_type_id: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: '.index-threshold', - doc_count: 2, - }, - { - key: 'logs.alert.document.count', - doc_count: 1, - }, - { - key: 'document.test.', - doc_count: 1, - }, + { key: '.index-threshold', doc_count: 2 }, + { key: 'logs.alert.document.count', doc_count: 1 }, + { key: 'document.test.', doc_count: 1 }, ], }, }, @@ -1527,7 +1556,7 @@ describe('event log telemetry', () => { const loggerCall = logger.warn.mock.calls[0][0]; const loggerMeta = logger.warn.mock.calls[0][1]; expect(loggerCall as string).toMatchInlineSnapshot( - `"Error executing alerting telemetry task: getExecutionsTimeoutsPerDayCount - {}"` + `"Error executing alerting telemetry task: getExecutionsTimeoutsPerDayCount - Error: oh no"` ); expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); expect(loggerMeta?.error?.stack_trace).toBeDefined(); @@ -1538,5 +1567,61 @@ describe('event log telemetry', () => { hasErrors: true, }); }); + + it('should return empty results and log debug log if query throws search_phase_execution_exception error', async () => { + esClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getExecutionTimeoutsPerDayCount({ + esClient, + eventLogIndex: 'test', + logger, + }); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(2); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getExecutionTimeoutsPerDayCount - {\"index\":\"test\",\"size\":0,\"body\":{\"query\":{\"bool\":{\"filter\":{\"bool\":{\"must\":[{\"term\":{\"event.action\":\"execute-timeout\"}},{\"term\":{\"event.provider\":\"alerting\"}},{\"range\":{\"@timestamp\":{\"gte\":\"now-1d\"}}}]}}}},\"aggs\":{\"by_rule_type_id\":{\"terms\":{\"field\":\"rule.category\",\"size\":33}}}}}` + ); + expect(loggerCalls.debug[1][0]).toMatchInlineSnapshot(` + "Error executing alerting telemetry task: getExecutionsTimeoutsPerDayCount - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[1][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.debug[1][1]?.error?.stack_trace).toBeDefined(); + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toStrictEqual({ + hasErrors: true, + errorMessage: 'no_shard_available_action_exception', + countExecutionTimeouts: 0, + countExecutionTimeoutsByType: {}, + }); + }); }); }); diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.ts index df76929ca5d50..70d8c5d7cb196 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_event_log.ts @@ -22,6 +22,7 @@ import { } from '../alerting_usage_collector'; import { replaceDotSymbols } from './replace_dots_with_underscores'; import { parseSimpleRuleTypeBucket } from './parse_simple_rule_type_bucket'; +import { parseAndLogError } from './parse_and_log_error'; const Millis2Nanos = 1000 * 1000; const percentileFieldNameMapping: Record = { @@ -189,14 +190,8 @@ export async function getExecutionsPerDayCount({ ), }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - logger.warn( - `Error executing alerting telemetry task: getExecutionsPerDayCount - ${JSON.stringify(err)}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err.stack }, - } - ); + const errorMessage = parseAndLogError(err, `getExecutionsPerDayCount`, logger); + return { hasErrors: true, errorMessage, @@ -262,17 +257,8 @@ export async function getExecutionTimeoutsPerDayCount({ countExecutionTimeoutsByType: parseSimpleRuleTypeBucket(aggregations.by_rule_type_id.buckets), }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - - logger.warn( - `Error executing alerting telemetry task: getExecutionsTimeoutsPerDayCount - ${JSON.stringify( - err - )}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err.stack }, - } - ); + const errorMessage = parseAndLogError(err, `getExecutionsTimeoutsPerDayCount`, logger); + return { hasErrors: true, errorMessage, diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts index 7c06e9867dae3..3173f17cd5288 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts @@ -5,7 +5,9 @@ * 2.0. */ +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging-mocks'; import { getTotalCountAggregations, getTotalCountInUse, @@ -17,7 +19,7 @@ import { ISavedObjectsRepository } from '@kbn/core/server'; const elasticsearch = elasticsearchServiceMock.createStart(); const esClient = elasticsearch.client.asInternalUser; -const logger: ReturnType = loggingSystemMock.createLogger(); +let logger: MockedLogger; const savedObjectsClient = savedObjectsClientMock.create() as unknown as ISavedObjectsRepository; const thrownError = new Error('Fail'); @@ -101,6 +103,7 @@ const mockedResponse = { describe('kibana index telemetry', () => { beforeEach(() => { jest.resetAllMocks(); + logger = loggerMock.create(); }); describe('getTotalCountAggregations', () => { @@ -108,37 +111,16 @@ describe('kibana index telemetry', () => { esClient.search.mockResponseOnce({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 4, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 4, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { by_rule_type_id: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: '.index-threshold', - doc_count: 2, - }, - { - key: 'logs.alert.document.count', - doc_count: 1, - }, - { - key: 'document.test.', - doc_count: 1, - }, + { key: '.index-threshold', doc_count: 2 }, + { key: 'logs.alert.document.count', doc_count: 1 }, + { key: 'document.test.', doc_count: 1 }, ], }, by_execution_status: { @@ -259,11 +241,7 @@ describe('kibana index telemetry', () => { }, }); - const telemetry = await getTotalCountAggregations({ - esClient, - alertIndex: 'test', - logger, - }); + const telemetry = await getTotalCountAggregations({ esClient, alertIndex: 'test', logger }); expect(esClient.search).toHaveBeenCalledTimes(1); @@ -333,17 +311,13 @@ describe('kibana index telemetry', () => { test('should return empty results and log warning if query throws error', async () => { esClient.search.mockRejectedValueOnce(new Error('oh no')); - const telemetry = await getTotalCountAggregations({ - esClient, - alertIndex: 'test', - logger, - }); + const telemetry = await getTotalCountAggregations({ esClient, alertIndex: 'test', logger }); expect(esClient.search).toHaveBeenCalledTimes(1); const loggerCall = logger.warn.mock.calls[0][0]; const loggerMeta = logger.warn.mock.calls[0][1]; expect(loggerCall as string).toMatchInlineSnapshot( - `"Error executing alerting telemetry task: getTotalCountAggregations - {}"` + `"Error executing alerting telemetry task: getTotalCountAggregations - Error: oh no"` ); expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); expect(loggerMeta?.error?.stack_trace).toBeDefined(); @@ -394,6 +368,98 @@ describe('kibana index telemetry', () => { count_connector_types_by_consumers: {}, }); }); + + test('should return empty results and log debug log if query throws search_phase_execution_exception error', async () => { + esClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getTotalCountAggregations({ esClient, alertIndex: 'test', logger }); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(2); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getTotalCountAggregations - {\"index\":\"test\",\"size\":0,\"body\":{\"query\":{\"bool\":{\"filter\":[{\"term\":{\"type\":\"alert\"}}]}},\"runtime_mappings\":{\"rule_action_count\":{\"type\":\"long\",\"script\":{\"source\":\"\\n def alert = params._source['alert'];\\n if (alert != null) {\\n def actions = alert.actions;\\n if (actions != null) {\\n emit(actions.length);\\n } else {\\n emit(0);\\n }\\n }\"}},\"rule_schedule_interval\":{\"type\":\"long\",\"script\":{\"source\":\"\\n int parsed = 0;\\n if (doc['alert.schedule.interval'].size() > 0) {\\n def interval = doc['alert.schedule.interval'].value;\\n\\n if (interval.length() > 1) {\\n // get last char\\n String timeChar = interval.substring(interval.length() - 1);\\n // remove last char\\n interval = interval.substring(0, interval.length() - 1);\\n\\n if (interval.chars().allMatch(Character::isDigit)) {\\n // using of regex is not allowed in painless language\\n parsed = Integer.parseInt(interval);\\n\\n if (timeChar.equals(\\\"s\\\")) {\\n parsed = parsed;\\n } else if (timeChar.equals(\\\"m\\\")) {\\n parsed = parsed * 60;\\n } else if (timeChar.equals(\\\"h\\\")) {\\n parsed = parsed * 60 * 60;\\n } else if (timeChar.equals(\\\"d\\\")) {\\n parsed = parsed * 24 * 60 * 60;\\n }\\n emit(parsed);\\n }\\n }\\n }\\n emit(parsed);\\n \"}},\"rule_throttle_interval\":{\"type\":\"long\",\"script\":{\"source\":\"\\n int parsed = 0;\\n if (doc['alert.throttle'].size() > 0) {\\n def throttle = doc['alert.throttle'].value;\\n\\n if (throttle.length() > 1) {\\n // get last char\\n String timeChar = throttle.substring(throttle.length() - 1);\\n // remove last char\\n throttle = throttle.substring(0, throttle.length() - 1);\\n\\n if (throttle.chars().allMatch(Character::isDigit)) {\\n // using of regex is not allowed in painless language\\n parsed = Integer.parseInt(throttle);\\n\\n if (timeChar.equals(\\\"s\\\")) {\\n parsed = parsed;\\n } else if (timeChar.equals(\\\"m\\\")) {\\n parsed = parsed * 60;\\n } else if (timeChar.equals(\\\"h\\\")) {\\n parsed = parsed * 60 * 60;\\n } else if (timeChar.equals(\\\"d\\\")) {\\n parsed = parsed * 24 * 60 * 60;\\n }\\n emit(parsed);\\n }\\n }\\n }\\n emit(parsed);\\n \"}},\"rule_with_tags\":{\"type\":\"long\",\"script\":{\"source\":\"\\n def rule = params._source['alert'];\\n if (rule != null && rule.tags != null) {\\n if (rule.tags.size() > 0) {\\n emit(1);\\n } else {\\n emit(0);\\n }\\n }\"}},\"rule_snoozed\":{\"type\":\"long\",\"script\":{\"source\":\"\\n def rule = params._source['alert'];\\n if (rule != null && rule.snoozeSchedule != null) {\\n if (rule.snoozeSchedule.size() > 0) {\\n emit(1);\\n } else {\\n emit(0);\\n }\\n }\"}},\"rule_muted\":{\"type\":\"long\",\"script\":{\"source\":\"\\n if (doc['alert.muteAll'].value == true) {\\n emit(1);\\n } else {\\n emit(0);\\n }\"}},\"rule_with_muted_alerts\":{\"type\":\"long\",\"script\":{\"source\":\"\\n def rule = params._source['alert'];\\n if (rule != null && rule.mutedInstanceIds != null) {\\n if (rule.mutedInstanceIds.size() > 0) {\\n emit(1);\\n } else {\\n emit(0);\\n }\\n }\"}}},\"aggs\":{\"by_rule_type_id\":{\"terms\":{\"field\":\"alert.alertTypeId\",\"size\":33}},\"max_throttle_time\":{\"max\":{\"field\":\"rule_throttle_interval\"}},\"min_throttle_time\":{\"min\":{\"field\":\"rule_throttle_interval\"}},\"avg_throttle_time\":{\"avg\":{\"field\":\"rule_throttle_interval\"}},\"max_interval_time\":{\"max\":{\"field\":\"rule_schedule_interval\"}},\"min_interval_time\":{\"min\":{\"field\":\"rule_schedule_interval\"}},\"avg_interval_time\":{\"avg\":{\"field\":\"rule_schedule_interval\"}},\"max_actions_count\":{\"max\":{\"field\":\"rule_action_count\"}},\"min_actions_count\":{\"min\":{\"field\":\"rule_action_count\"}},\"avg_actions_count\":{\"avg\":{\"field\":\"rule_action_count\"}},\"by_execution_status\":{\"terms\":{\"field\":\"alert.executionStatus.status\"}},\"by_notify_when\":{\"terms\":{\"field\":\"alert.notifyWhen\"}},\"connector_types_by_consumers\":{\"terms\":{\"field\":\"alert.consumer\"},\"aggs\":{\"actions\":{\"nested\":{\"path\":\"alert.actions\"},\"aggs\":{\"connector_types\":{\"terms\":{\"field\":\"alert.actions.actionTypeId\"}}}}}},\"by_search_type\":{\"terms\":{\"field\":\"alert.params.searchType\"}},\"sum_rules_with_tags\":{\"sum\":{\"field\":\"rule_with_tags\"}},\"sum_rules_snoozed\":{\"sum\":{\"field\":\"rule_snoozed\"}},\"sum_rules_muted\":{\"sum\":{\"field\":\"rule_muted\"}},\"sum_rules_with_muted_alerts\":{\"sum\":{\"field\":\"rule_with_muted_alerts\"}}}}}` + ); + expect(loggerCalls.debug[1][0]).toMatchInlineSnapshot(` + "Error executing alerting telemetry task: getTotalCountAggregations - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[1][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.debug[1][1]?.error?.stack_trace).toBeDefined(); + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toEqual({ + errorMessage: 'no_shard_available_action_exception', + hasErrors: true, + connectors_per_alert: { + avg: 0, + max: 0, + min: 0, + }, + count_by_type: {}, + count_rules_by_execution_status: { + success: 0, + error: 0, + warning: 0, + }, + count_rules_with_tags: 0, + count_rules_by_notify_when: { + on_action_group_change: 0, + on_active_alert: 0, + on_throttle_interval: 0, + }, + count_rules_snoozed: 0, + count_rules_muted: 0, + count_rules_with_muted_alerts: 0, + count_total: 0, + schedule_time: { + avg: '0s', + max: '0s', + min: '0s', + }, + schedule_time_number_s: { + avg: 0, + max: 0, + min: 0, + }, + throttle_time: { + avg: '0s', + max: '0s', + min: '0s', + }, + throttle_time_number_s: { + avg: 0, + max: 0, + min: 0, + }, + count_connector_types_by_consumers: {}, + }); + }); }); describe('getTotalCountInUse', () => { @@ -401,66 +467,32 @@ describe('kibana index telemetry', () => { esClient.search.mockResponseOnce({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 4, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 4, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { namespaces_count: { value: 1 }, by_rule_type_id: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: '.index-threshold', - doc_count: 2, - }, - { - key: 'logs.alert.document.count', - doc_count: 1, - }, - { - key: 'document.test.', - doc_count: 1, - }, + { key: '.index-threshold', doc_count: 2 }, + { key: 'logs.alert.document.count', doc_count: 1 }, + { key: 'document.test.', doc_count: 1 }, ], }, by_search_type: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: 'esQuery', - doc_count: 0, - }, - { - key: 'searchSource', - doc_count: 1, - }, - { - key: 'esqlQuery', - doc_count: 3, - }, + { key: 'esQuery', doc_count: 0 }, + { key: 'searchSource', doc_count: 1 }, + { key: 'esqlQuery', doc_count: 3 }, ], }, }, }); - const telemetry = await getTotalCountInUse({ - esClient, - alertIndex: 'test', - logger, - }); + const telemetry = await getTotalCountInUse({ esClient, alertIndex: 'test', logger }); expect(esClient.search).toHaveBeenCalledTimes(1); @@ -483,17 +515,13 @@ describe('kibana index telemetry', () => { test('should return empty results and log warning if query throws error', async () => { esClient.search.mockRejectedValueOnce(new Error('oh no')); - const telemetry = await getTotalCountInUse({ - esClient, - alertIndex: 'test', - logger, - }); + const telemetry = await getTotalCountInUse({ esClient, alertIndex: 'test', logger }); expect(esClient.search).toHaveBeenCalledTimes(1); const loggerCall = logger.warn.mock.calls[0][0]; const loggerMeta = logger.warn.mock.calls[0][1]; expect(loggerCall as string).toMatchInlineSnapshot( - `"Error executing alerting telemetry task: getTotalCountInUse - {}"` + `"Error executing alerting telemetry task: getTotalCountInUse - Error: oh no"` ); expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); expect(loggerMeta?.error?.stack_trace).toBeDefined(); @@ -505,6 +533,59 @@ describe('kibana index telemetry', () => { hasErrors: true, }); }); + + test('should return empty results and log debug log if query throws search_phase_execution_exception error', async () => { + esClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getTotalCountInUse({ esClient, alertIndex: 'test', logger }); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(2); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getTotalCountInUse - {\"index\":\"test\",\"size\":0,\"body\":{\"query\":{\"bool\":{\"filter\":[{\"term\":{\"type\":\"alert\"}},{\"term\":{\"alert.enabled\":true}}]}},\"aggs\":{\"namespaces_count\":{\"cardinality\":{\"field\":\"namespaces\"}},\"by_rule_type_id\":{\"terms\":{\"field\":\"alert.alertTypeId\",\"size\":33}},\"by_search_type\":{\"terms\":{\"field\":\"alert.params.searchType\"}}}}}` + ); + expect(loggerCalls.debug[1][0]).toMatchInlineSnapshot(` + "Error executing alerting telemetry task: getTotalCountInUse - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[1][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.debug[1][1]?.error?.stack_trace).toBeDefined(); + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toStrictEqual({ + countByType: {}, + countNamespaces: 0, + countTotal: 0, + errorMessage: 'no_shard_available_action_exception', + hasErrors: true, + }); + }); }); describe('getMWTelemetry', () => { @@ -515,10 +596,7 @@ describe('kibana index telemetry', () => { yield mockedResponse; }), }); - const telemetry = await getMWTelemetry({ - savedObjectsClient, - logger, - }); + const telemetry = await getMWTelemetry({ savedObjectsClient, logger }); expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, @@ -533,67 +611,63 @@ describe('kibana index telemetry', () => { hasErrors: false, }); }); - }); - test('should throw the error', async () => { - savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ - close: jest.fn(), - find: jest.fn().mockImplementation(async function* () { - throw thrownError; - }), - }); + test('should return empty results and log warning if query throws error', async () => { + savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ + close: jest.fn(), + find: jest.fn().mockImplementation(async function* () { + throw thrownError; + }), + }); - const telemetry = await getMWTelemetry({ - savedObjectsClient, - logger, - }); + const telemetry = await getMWTelemetry({ savedObjectsClient, logger }); - expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ - type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - namespaces: ['*'], - perPage: 100, - fields: ['rRule', 'scopedQuery'], - }); + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + namespaces: ['*'], + perPage: 100, + fields: ['rRule', 'scopedQuery'], + }); - expect(telemetry).toStrictEqual({ - count_mw_total: 0, - count_mw_with_repeat_toggle_on: 0, - count_mw_with_filter_alert_toggle_on: 0, - hasErrors: true, - errorMessage: 'Fail', - }); - expect(logger.warn).toHaveBeenCalled(); - const loggerCall = logger.warn.mock.calls[0][0]; - const loggerMeta = logger.warn.mock.calls[0][1]; - expect(loggerCall).toBe('Error executing alerting telemetry task: getTotalMWCount - {}'); - expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); - expect(loggerMeta?.error?.stack_trace).toBeDefined(); - }); + expect(telemetry).toStrictEqual({ + count_mw_total: 0, + count_mw_with_repeat_toggle_on: 0, + count_mw_with_filter_alert_toggle_on: 0, + hasErrors: true, + errorMessage: 'Fail', + }); - test('should stop on MW max limit count', async () => { - savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ - close: jest.fn(), - find: jest.fn().mockImplementation(async function* () { - yield mockedResponse; - }), - }); - const telemetry = await getMWTelemetry({ - savedObjectsClient, - logger, - maxDocuments: 1, + expect(logger.warn).toHaveBeenCalled(); + const loggerCall = logger.warn.mock.calls[0][0]; + const loggerMeta = logger.warn.mock.calls[0][1]; + expect(loggerCall).toBe( + 'Error executing alerting telemetry task: getTotalMWCount - Error: Fail' + ); + expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerMeta?.error?.stack_trace).toBeDefined(); }); - expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ - type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, - namespaces: ['*'], - perPage: 100, - fields: ['rRule', 'scopedQuery'], - }); - expect(telemetry).toStrictEqual({ - count_mw_total: 2, - count_mw_with_repeat_toggle_on: 1, - count_mw_with_filter_alert_toggle_on: 1, - hasErrors: false, + test('should stop on MW max limit count', async () => { + savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ + close: jest.fn(), + find: jest.fn().mockImplementation(async function* () { + yield mockedResponse; + }), + }); + const telemetry = await getMWTelemetry({ savedObjectsClient, logger, maxDocuments: 1 }); + + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + namespaces: ['*'], + perPage: 100, + fields: ['rRule', 'scopedQuery'], + }); + expect(telemetry).toStrictEqual({ + count_mw_total: 2, + count_mw_with_repeat_toggle_on: 1, + count_mw_with_filter_alert_toggle_on: 1, + hasErrors: false, + }); }); }); }); diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.ts index 756512815d901..de9238f2eb904 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_kibana.ts @@ -25,6 +25,7 @@ import { parseSimpleRuleTypeBucket } from './parse_simple_rule_type_bucket'; import { groupRulesBySearchType } from './group_rules_by_search_type'; import { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE } from '../../../common'; import { MaintenanceWindowAttributes } from '../../data/maintenance_window/types'; +import { parseAndLogError } from './parse_and_log_error'; interface Opts { esClient: ElasticsearchClient; @@ -376,15 +377,8 @@ export async function getTotalCountAggregations({ }, }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); + const errorMessage = parseAndLogError(err, `getTotalCountAggregations`, logger); - logger.warn( - `Error executing alerting telemetry task: getTotalCountAggregations - ${JSON.stringify(err)}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err.stack }, - } - ); return { hasErrors: true, errorMessage, @@ -491,14 +485,8 @@ export async function getTotalCountInUse({ countNamespaces: aggregations.namespaces_count.value ?? 0, }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - logger.warn( - `Error executing alerting telemetry task: getTotalCountInUse - ${JSON.stringify(err)}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err.stack }, - } - ); + const errorMessage = parseAndLogError(err, `getTotalCountInUse`, logger); + return { hasErrors: true, errorMessage, @@ -548,14 +536,8 @@ export async function getMWTelemetry({ count_mw_with_filter_alert_toggle_on: countMWWithFilterAlertToggleON, }; } catch (err) { - const errorMessage = err?.message ? err.message : err.toString(); - logger.warn( - `Error executing alerting telemetry task: getTotalMWCount - ${JSON.stringify(err)}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err?.stack }, - } - ); + const errorMessage = parseAndLogError(err, `getTotalMWCount`, logger); + return { hasErrors: true, errorMessage, diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.test.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.test.ts index 3061571ab7ed9..bc18438a04a56 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.test.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.test.ts @@ -5,7 +5,9 @@ * 2.0. */ +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { MockedLogger, loggerMock } from '@kbn/logging-mocks'; import { getFailedAndUnrecognizedTasksPerDay, parseBucket, @@ -13,11 +15,12 @@ import { const elasticsearch = elasticsearchServiceMock.createStart(); const esClient = elasticsearch.client.asInternalUser; -const logger: ReturnType = loggingSystemMock.createLogger(); +let logger: MockedLogger; describe('task manager telemetry', () => { beforeEach(() => { jest.resetAllMocks(); + logger = loggerMock.create(); }); describe('parseBucket', () => { @@ -145,20 +148,8 @@ describe('task manager telemetry', () => { esClient.search.mockResponse({ took: 4, timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 40, - relation: 'eq', - }, - max_score: null, - hits: [], - }, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 40, relation: 'eq' }, max_score: null, hits: [] }, aggregations: { by_status: { doc_count_error_upper_bound: 0, @@ -243,7 +234,7 @@ describe('task manager telemetry', () => { const loggerCall = logger.warn.mock.calls[0][0]; const loggerMeta = logger.warn.mock.calls[0][1]; expect(loggerCall as string).toMatchInlineSnapshot( - `"Error executing alerting telemetry task: getFailedAndUnrecognizedTasksPerDay - {}"` + `"Error executing alerting telemetry task: getFailedAndUnrecognizedTasksPerDay - Error: oh no"` ); expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); expect(loggerMeta?.error?.stack_trace).toBeDefined(); @@ -255,5 +246,62 @@ describe('task manager telemetry', () => { countFailedAndUnrecognizedTasksByStatusByType: {}, }); }); + + test('should return empty results and log debug log if query throws search_phase_execution_exception error', async () => { + esClient.search.mockRejectedValueOnce( + new errors.ResponseError({ + warnings: [], + // eslint-disable-next-line @typescript-eslint/no-explicit-any + meta: {} as any, + body: { + error: { + root_cause: [], + type: 'search_phase_execution_exception', + reason: 'no_shard_available_action_exception', + phase: 'fetch', + grouped: true, + failed_shards: [], + caused_by: { + type: 'no_shard_available_action_exception', + reason: 'This is the nested reason', + }, + }, + }, + statusCode: 503, + headers: {}, + }) + ); + + const telemetry = await getFailedAndUnrecognizedTasksPerDay({ + esClient, + taskManagerIndex: 'test', + logger, + }); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + const loggerCalls = loggingSystemMock.collect(logger); + expect(loggerCalls.debug).toHaveLength(2); + expect(loggerCalls.debug[0][0]).toEqual( + `query for getFailedAndUnrecognizedTasksPerDay - {\"index\":\"test\",\"size\":0,\"body\":{\"query\":{\"bool\":{\"must\":[{\"bool\":{\"should\":[{\"term\":{\"task.status\":\"unrecognized\"}},{\"term\":{\"task.status\":\"failed\"}}]}},{\"wildcard\":{\"task.taskType\":{\"value\":\"alerting:*\"}}},{\"range\":{\"task.runAt\":{\"gte\":\"now-1d\"}}}]}},\"aggs\":{\"by_status\":{\"terms\":{\"field\":\"task.status\",\"size\":10},\"aggs\":{\"by_task_type\":{\"terms\":{\"field\":\"task.taskType\",\"size\":33}}}}}}}` + ); + expect(loggerCalls.debug[1][0]).toMatchInlineSnapshot(` + "Error executing alerting telemetry task: getFailedAndUnrecognizedTasksPerDay - ResponseError: search_phase_execution_exception + Caused by: + no_shard_available_action_exception: This is the nested reason" + `); + // logger meta + expect(loggerCalls.debug[1][1]?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerCalls.debug[1][1]?.error?.stack_trace).toBeDefined(); + expect(loggerCalls.warn).toHaveLength(0); + + expect(telemetry).toStrictEqual({ + errorMessage: 'no_shard_available_action_exception', + hasErrors: true, + countFailedAndUnrecognizedTasks: 0, + countFailedAndUnrecognizedTasksByStatus: {}, + countFailedAndUnrecognizedTasksByStatusByType: {}, + }); + }); }); }); diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.ts index f3741a086bf9b..9658bbcabf1af 100644 --- a/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.ts +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/get_telemetry_from_task_manager.ts @@ -14,6 +14,7 @@ import type { import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { replaceDotSymbols } from './replace_dots_with_underscores'; import { NUM_ALERTING_RULE_TYPES } from '../alerting_usage_collector'; +import { parseAndLogError } from './parse_and_log_error'; interface Opts { esClient: ElasticsearchClient; @@ -122,16 +123,8 @@ export async function getFailedAndUnrecognizedTasksPerDay({ countFailedAndUnrecognizedTasks: totalFailedAndUnrecognizedTasks ?? 0, }; } catch (err) { - const errorMessage = err && err.message ? err.message : err.toString(); - logger.warn( - `Error executing alerting telemetry task: getFailedAndUnrecognizedTasksPerDay - ${JSON.stringify( - err - )}`, - { - tags: ['alerting', 'telemetry-failed'], - error: { stack_trace: err.stack }, - } - ); + const errorMessage = parseAndLogError(err, `getFailedAndUnrecognizedTasksPerDay`, logger); + return { hasErrors: true, errorMessage, diff --git a/x-pack/platform/plugins/shared/alerting/server/usage/lib/parse_and_log_error.ts b/x-pack/platform/plugins/shared/alerting/server/usage/lib/parse_and_log_error.ts new file mode 100644 index 0000000000000..2f7eaccbd7cb7 --- /dev/null +++ b/x-pack/platform/plugins/shared/alerting/server/usage/lib/parse_and_log_error.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '@kbn/core/server'; + +export function parseAndLogError(err: Error, errType: string, logger: Logger): string { + const errorMessage = err && err.message ? err.message : err.toString(); + let returnedErrorMessage = errorMessage; + + const errorStr = JSON.stringify(err); + const logMessage = `Error executing alerting telemetry task: ${errType} - ${err}`; + const logOptions = { + tags: ['alerting', 'telemetry-failed'], + error: { stack_trace: err.stack }, + }; + + // If error string contains "no_shard_available_action_exception", debug log it + if (errorStr.includes('no_shard_available_action_exception')) { + // the no_shard_available_action_exception can be wordy and the error message returned from this function + // gets stored in the task state so lets simplify + returnedErrorMessage = 'no_shard_available_action_exception'; + if (logger.isLevelEnabled('debug')) { + logger.debug(logMessage, logOptions); + } + } else { + logger.warn(logMessage, logOptions); + } + + return returnedErrorMessage; +} diff --git a/x-pack/platform/plugins/shared/cases/public/components/custom_fields/form.test.tsx b/x-pack/platform/plugins/shared/cases/public/components/custom_fields/form.test.tsx index 7bf7d95e48b25..2492dbb3fd32a 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/custom_fields/form.test.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/custom_fields/form.test.tsx @@ -18,7 +18,8 @@ import userEvent from '@testing-library/user-event'; import { customFieldsConfigurationMock } from '../../containers/mock'; import type { FormState } from '../configure_cases/flyout'; -describe('CustomFieldsForm ', () => { +// FLAKY: https://github.com/elastic/kibana/issues/208415 +describe.skip('CustomFieldsForm ', () => { let appMockRender: AppMockRenderer; const onChange = jest.fn(); diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/source_definition.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/source_definition.ts index eab219792295c..7de0e7426c8d0 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/source_definition.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/source_definition.ts @@ -46,7 +46,7 @@ export async function storeSourceDefinition({ const sources = await runESQLQuery('fetch source definition for conflict check', { esClient, - query: `FROM ${DEFINITIONS_ALIAS} METADATA _id | WHERE definition_type == "source" AND _id == "${source.type_id}:${source.id}" | KEEP _id`, + query: `FROM ${DEFINITIONS_ALIAS} METADATA _id | WHERE definition_type == "source" AND _id == "${source.type_id}:${source.id}" | KEEP _id | LIMIT 1000`, logger, }); @@ -88,7 +88,7 @@ export async function readSourceDefinitions( 'fetch all source definitions', { esClient, - query: `FROM ${DEFINITIONS_ALIAS} METADATA _source | WHERE definition_type == "source" ${typeFilter} | KEEP _source`, + query: `FROM ${DEFINITIONS_ALIAS} METADATA _source | WHERE definition_type == "source" ${typeFilter} | KEEP _source | LIMIT 1000`, logger, } ); diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/type_definition.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/type_definition.ts index 9b362a8df2b3e..c709c5453b55e 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/type_definition.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/definitions/type_definition.ts @@ -29,7 +29,7 @@ export async function storeTypeDefinition({ const types = await runESQLQuery('fetch type definition for conflict check', { esClient, - query: `FROM ${DEFINITIONS_ALIAS} METADATA _id | WHERE definition_type == "type" AND _id == "${type.id}" | KEEP _id`, + query: `FROM ${DEFINITIONS_ALIAS} METADATA _id | WHERE definition_type == "type" AND _id == "${type.id}" | KEEP _id | LIMIT 1000`, logger, }); @@ -65,7 +65,7 @@ export async function readTypeDefinitions( 'fetch all type definitions', { esClient, - query: `FROM ${DEFINITIONS_ALIAS} METADATA _source | WHERE definition_type == "type" | KEEP _source`, + query: `FROM ${DEFINITIONS_ALIAS} METADATA _source | WHERE definition_type == "type" | KEEP _source | LIMIT 1000`, logger, } ); @@ -84,7 +84,7 @@ export async function readTypeDefinitionById( 'fetch type definition by ID', { esClient, - query: `FROM ${DEFINITIONS_ALIAS} METADATA _id,_source | WHERE definition_type == "type" AND _id == "${id}" | KEEP _source`, + query: `FROM ${DEFINITIONS_ALIAS} METADATA _id,_source | WHERE definition_type == "type" AND _id == "${id}" | KEEP _source | LIMIT 1000`, logger, } ); diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.test.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.test.ts index 04a391ef0026f..7bddd16a1e605 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.test.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.test.ts @@ -26,7 +26,9 @@ describe('getEntityCountQuery', () => { end: '2024-11-20T20:00:00.000Z', }); - expect(query).toEqual('FROM logs-* | STATS BY service.name::keyword | STATS count = COUNT()'); + expect(query).toEqual( + 'FROM logs-* | STATS BY service.name::keyword | STATS count = COUNT() | LIMIT 1000' + ); expect(filter).toEqual({ bool: { @@ -147,7 +149,8 @@ describe('getEntityCountQuery', () => { 'EVAL entity.id = CASE(is_source_0, service_name::keyword, is_source_1, service.name::keyword) | ' + 'WHERE entity.id IS NOT NULL | ' + 'STATS BY entity.id | ' + - 'STATS count = COUNT()' + 'STATS count = COUNT() | ' + + 'LIMIT 1000' ); expect(filter).toEqual({ diff --git a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.ts b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.ts index 930ee15be61b8..ae7905679b7d0 100644 --- a/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.ts +++ b/x-pack/platform/plugins/shared/entity_manager/server/lib/v2/queries/entity_count.ts @@ -129,6 +129,7 @@ export function getEntityCountQuery({ idEvalCommand({ sources }), whereCommand({ sources }), statsCommand({ sources }), + `LIMIT 1000`, ]); const filter = dslFilter({ sources, filters, start, end }); diff --git a/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts b/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts index b0a1fa29e36c9..7866e847897b9 100644 --- a/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts +++ b/x-pack/platform/plugins/shared/fleet/common/experimental_features.ts @@ -11,6 +11,7 @@ const _allowedExperimentalValues = { showExperimentalShipperOptions: false, useSpaceAwareness: false, enableAutomaticAgentUpgrades: false, + enableSyncIntegrationsOnRemote: false, }; /** diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts index b47a393013eda..76bd16d062197 100644 --- a/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/common/types/models/output.ts @@ -65,7 +65,11 @@ export interface NewRemoteElasticsearchOutput extends NewBaseOutput { service_token?: string | null; secrets?: { service_token?: OutputSecret; + kibana_api_key?: OutputSecret; }; + sync_integrations?: boolean; + kibana_url?: string | null; + kibana_api_key?: string | null; } export interface NewLogstashOutput extends NewBaseOutput { diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts index eab5f3ccecac5..0ff4a2e07e431 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts @@ -100,13 +100,14 @@ export function useSetupTechnology({ ); useEffect(() => { const shouldBeDefault = - isOnlyAgentlessIntegration(packageInfo, integrationToEnable) || - isAgentlessSetupDefault(packageInfo, integrationToEnable) + isAgentlessEnabled && + (isOnlyAgentlessIntegration(packageInfo, integrationToEnable) || + isAgentlessSetupDefault(packageInfo, integrationToEnable)) ? SetupTechnology.AGENTLESS : SetupTechnology.AGENT_BASED; setDefaultSetupTechnology(shouldBeDefault); setSelectedSetupTechnology(shouldBeDefault); - }, [packageInfo, integrationToEnable]); + }, [isAgentlessEnabled, packageInfo, integrationToEnable]); const agentlessPolicyName = getAgentlessAgentPolicyNameFromPackagePolicyName(packagePolicy.name); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx index 05d66fff15c8c..69545da581b8c 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.test.tsx @@ -319,7 +319,9 @@ describe('EditOutputFlyout', () => { }); it('should render the flyout if the output provided is a remote ES output', async () => { - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); + jest + .spyOn(ExperimentalFeaturesService, 'get') + .mockReturnValue({ enableSyncIntegrationsOnRemote: true } as any); mockedUseFleetStatus.mockReturnValue({ isLoading: false, @@ -333,6 +335,8 @@ describe('EditOutputFlyout', () => { id: 'outputR', is_default: false, is_default_monitoring: false, + kibana_url: 'http://localhost', + sync_integrations: true, }); remoteEsOutputLabels.forEach((label) => { @@ -345,10 +349,18 @@ describe('EditOutputFlyout', () => { ); expect(utils.queryByTestId('serviceTokenSecretInput')).not.toBeNull(); + + expect(utils.queryByTestId('kibanaAPIKeyCallout')).not.toBeNull(); + expect( + (utils.getByTestId('settingsOutputsFlyout.kibanaURLInput') as HTMLInputElement).value + ).toEqual('http://localhost'); + expect(utils.queryByTestId('kibanaAPIKeySecretInput')).not.toBeNull(); }); it('should populate secret service token input with plain text value when editing remote ES output', async () => { - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({} as any); + jest + .spyOn(ExperimentalFeaturesService, 'get') + .mockReturnValue({ enableSyncIntegrationsOnRemote: true } as any); mockedUseFleetStatus.mockReturnValue({ isLoading: false, @@ -364,11 +376,13 @@ describe('EditOutputFlyout', () => { is_default_monitoring: false, service_token: '1234', hosts: ['https://localhost:9200'], + kibana_api_key: 'key', }); expect((utils.getByTestId('serviceTokenSecretInput') as HTMLInputElement).value).toEqual( '1234' ); + expect((utils.getByTestId('kibanaAPIKeySecretInput') as HTMLInputElement).value).toEqual('key'); fireEvent.click(utils.getByText('Save and apply settings')); @@ -376,8 +390,9 @@ describe('EditOutputFlyout', () => { expect(mockSendPutOutput).toHaveBeenCalledWith( 'outputR', expect.objectContaining({ - secrets: { service_token: '1234' }, + secrets: { service_token: '1234', kibana_api_key: 'key' }, service_token: undefined, + kibana_api_key: undefined, }) ); }); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx index 658a42115381b..9aa5d5f830b64 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_remote_es.tsx @@ -6,12 +6,21 @@ */ import React, { useEffect } from 'react'; -import { EuiCallOut, EuiCodeBlock, EuiFieldText, EuiSpacer } from '@elastic/eui'; +import { + EuiCallOut, + EuiCodeBlock, + EuiFieldText, + EuiFormRow, + EuiSpacer, + EuiSwitch, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { MultiRowInput } from '../multi_row_input'; +import { ExperimentalFeaturesService } from '../../../../services'; + import type { OutputFormInputsType } from './use_output_form'; import { SecretFormRow } from './output_form_secret_form_row'; @@ -25,7 +34,9 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) const { inputs, useSecretsStorage, onToggleSecretStorage } = props; const [isConvertedToSecret, setIsConvertedToSecret] = React.useState({ serviceToken: false, + kibanaAPIKey: false, }); + const { enableSyncIntegrationsOnRemote } = ExperimentalFeaturesService.get(); const [isFirstLoad, setIsFirstLoad] = React.useState(true); @@ -34,16 +45,30 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) setIsFirstLoad(false); // populate the secret input with the value of the plain input in order to re-save the output with secret storage if (useSecretsStorage) { + let isServiceTokenSecret = false; if (inputs.serviceTokenInput.value && !inputs.serviceTokenSecretInput.value) { inputs.serviceTokenSecretInput.setValue(inputs.serviceTokenInput.value); inputs.serviceTokenInput.clear(); - setIsConvertedToSecret({ serviceToken: true }); + isServiceTokenSecret = true; + } + let isKibanaAPIKeySecret = false; + if (inputs.kibanaAPIKeyInput.value && !inputs.kibanaAPIKeySecretInput.value) { + inputs.kibanaAPIKeySecretInput.setValue(inputs.kibanaAPIKeyInput.value); + inputs.kibanaAPIKeyInput.clear(); + isKibanaAPIKeySecret = true; } + setIsConvertedToSecret({ + ...isConvertedToSecret, + serviceToken: isServiceTokenSecret, + kibanaAPIKey: isKibanaAPIKeySecret, + }); } }, [ useSecretsStorage, inputs.serviceTokenInput, inputs.serviceTokenSecretInput, + inputs.kibanaAPIKeyInput, + inputs.kibanaAPIKeySecretInput, isFirstLoad, setIsFirstLoad, isConvertedToSecret, @@ -52,10 +77,12 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) const onToggleSecretAndClearValue = (secretEnabled: boolean) => { if (secretEnabled) { inputs.serviceTokenInput.clear(); + inputs.kibanaAPIKeyInput.clear(); } else { inputs.serviceTokenSecretInput.setValue(''); + inputs.kibanaAPIKeySecretInput.setValue(''); } - setIsConvertedToSecret({ ...isConvertedToSecret, serviceToken: false }); + setIsConvertedToSecret({ ...isConvertedToSecret, serviceToken: false, kibanaAPIKey: false }); onToggleSecretStorage(secretEnabled); }; @@ -144,6 +171,132 @@ export const OutputFormRemoteEsSection: React.FunctionComponent = (props) + {enableSyncIntegrationsOnRemote ? ( + <> + + } + {...inputs.syncIntegrationsInput.formRowProps} + > + + } + /> + + + } + {...inputs.kibanaURLInput.formRowProps} + > + + + + {!useSecretsStorage ? ( + + } + {...inputs.kibanaAPIKeyInput.formRowProps} + useSecretsStorage={useSecretsStorage} + onToggleSecretStorage={onToggleSecretAndClearValue} + > + + + ) : ( + + + + )} + + + } + data-test-subj="kibanaAPIKeyCallout" + > + + {` POST /_security/api_key + { + "name": "integration_sync_api_key", + "role_descriptors": { + "integration_writer": { + "cluster": [], + "indices":[], + "applications": [{ + "application": "kibana-.kibana", + "privileges": ["feature_fleet.read", "feature_fleetv2.read"], + "resources": ["*"] + }] + } + } + }`} + + + + + ) : null} ); }; diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx index 2e17153ee34e7..888b815172bc2 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.test.tsx @@ -12,6 +12,8 @@ import { validateCATrustedFingerPrint, validateKafkaHeaders, validateKafkaHosts, + validateKibanaURL, + validateKibanaAPIKey, } from './output_form_validators'; describe('Output form validation', () => { @@ -130,6 +132,58 @@ describe('Output form validation', () => { }); }); + describe('validateKibanaURL', () => { + it('should not work with empty url', () => { + const res = validateKibanaURL('', true); + + expect(res).toEqual(['URL is required']); + }); + + it('should work with empty url if syncEnabled is false', () => { + const res = validateKibanaURL('', false); + + expect(res).toBeUndefined(); + }); + + it('should work with valid url', () => { + const res = validateKibanaURL('https://test.fr:9200', true); + + expect(res).toBeUndefined(); + }); + + it('should return an error with invalid url', () => { + const res = validateKibanaURL('toto', false); + + expect(res).toEqual(['Invalid URL']); + }); + + it('should return an error with url with invalid port', () => { + const res = validateKibanaURL('https://test.fr:qwerty9200', true); + + expect(res).toEqual(['Invalid URL']); + }); + + it('should return an error when invalid protocol', () => { + const res = validateKibanaURL('ftp://test.fr', false); + + expect(res).toEqual(['Invalid protocol']); + }); + }); + + describe('validateKibanaAPIKey', () => { + it('should not work with empty url', () => { + const res = validateKibanaAPIKey(''); + + expect(res).toEqual(['Kibana API Key is required']); + }); + + it('should work with valid url', () => { + const res = validateKibanaAPIKey('apikey'); + + expect(res).toBeUndefined(); + }); + }); + describe('validateLogstashHosts', () => { it('should not work without any urls', () => { const res = validateLogstashHosts([]); diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx index 5e3bf5b3725e9..14d2430f5121a 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_validators.tsx @@ -241,6 +241,35 @@ export function validateName(value: string) { } } +export function validateKibanaURL(val: string, syncEnabled: boolean) { + try { + if (syncEnabled && !val) { + return [ + i18n.translate('xpack.fleet.settings.outputForm.urlRequiredError', { + defaultMessage: 'URL is required', + }), + ]; + } else if (!val) { + return; + } else { + const urlParsed = new URL(val); + if (!['http:', 'https:'].includes(urlParsed.protocol)) { + return [ + i18n.translate('xpack.fleet.settings.outputForm.invalidProtocolError', { + defaultMessage: 'Invalid protocol', + }), + ]; + } + } + } catch (error) { + return [ + i18n.translate('xpack.fleet.settings.outputForm.invalidURLError', { + defaultMessage: 'Invalid URL', + }), + ]; + } +} + export function validateKafkaUsername(value: string) { if (!value || value === '') { return [ @@ -286,6 +315,18 @@ export function validateServiceToken(value: string) { export const validateServiceTokenSecret = toSecretValidator(validateServiceToken); +export function validateKibanaAPIKey(value: string) { + if (!value || value === '') { + return [ + i18n.translate('xpack.fleet.settings.outputForm.kibanaAPIKeyRequiredErrorMessage', { + defaultMessage: 'Kibana API Key is required', + }), + ]; + } +} + +export const validateKibanaAPIKeySecret = toSecretValidator(validateKibanaAPIKey); + export function validateSSLCertificate(value: string) { if (!value || value === '') { return [ diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx index e47e271872309..d2dfb92057289 100644 --- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx @@ -74,6 +74,9 @@ import { validateKafkaHosts, validateKafkaPartitioningGroupEvents, validateDynamicKafkaTopics, + validateKibanaURL, + validateKibanaAPIKey, + validateKibanaAPIKeySecret, } from './output_form_validators'; import { confirmUpdate } from './confirm_update'; @@ -97,6 +100,10 @@ export interface OutputFormInputsType { caTrustedFingerprintInput: ReturnType; serviceTokenInput: ReturnType; serviceTokenSecretInput: ReturnType; + syncIntegrationsInput: ReturnType; + kibanaURLInput: ReturnType; + kibanaAPIKeyInput: ReturnType; + kibanaAPIKeySecretInput: ReturnType; sslCertificateInput: ReturnType; sslKeyInput: ReturnType; sslKeySecretInput: ReturnType; @@ -262,7 +269,7 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu isDisabled('preset') ); - // Remtote ES inputs + // Remote ES inputs const serviceTokenInput = useInput( (output as NewRemoteElasticsearchOutput)?.service_token ?? '', validateServiceToken, @@ -274,6 +281,29 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu validateServiceTokenSecret, isDisabled('service_token') ); + + const syncIntegrationsInput = useSwitchInput( + (output as NewRemoteElasticsearchOutput)?.sync_integrations ?? false, + isDisabled('sync_integrations') + ); + + const kibanaAPIKeyInput = useInput( + (output as NewRemoteElasticsearchOutput)?.kibana_api_key ?? '', + syncIntegrationsInput.value ? validateKibanaAPIKey : undefined, + isDisabled('kibana_api_key') + ); + + const kibanaAPIKeySecretInput = useSecretInput( + (output as NewRemoteElasticsearchOutput)?.secrets?.kibana_api_key ?? '', + syncIntegrationsInput.value ? validateKibanaAPIKeySecret : undefined, + isDisabled('kibana_api_key') + ); + + const kibanaURLInput = useInput( + (output as NewRemoteElasticsearchOutput)?.kibana_url ?? '', + (val) => validateKibanaURL(val, syncIntegrationsInput.value), + isDisabled('kibana_url') + ); /* Shipper feature flag - currently depends on the content of the yaml # Enables the shipper: @@ -556,6 +586,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu caTrustedFingerprintInput, serviceTokenInput, serviceTokenSecretInput, + kibanaAPIKeyInput, + kibanaAPIKeySecretInput, + syncIntegrationsInput, + kibanaURLInput, sslCertificateInput, sslKeyInput, sslKeySecretInput, @@ -615,6 +649,9 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu const caTrustedFingerprintValid = caTrustedFingerprintInput.validate(); const serviceTokenValid = serviceTokenInput.validate(); const serviceTokenSecretValid = serviceTokenSecretInput.validate(); + const kibanaAPIKeyValid = kibanaAPIKeyInput.validate(); + const kibanaAPIKeySecretValid = kibanaAPIKeySecretInput.validate(); + const kibanaURLInputValid = kibanaURLInput.validate(); const sslCertificateValid = sslCertificateInput.validate(); const sslKeyValid = sslKeyInput.validate(); const sslKeySecretValid = sslKeySecretInput.validate(); @@ -666,7 +703,12 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu additionalYamlConfigValid && nameInputValid && ((serviceTokenInput.value && serviceTokenValid) || - (serviceTokenSecretInput.value && serviceTokenSecretValid)) + (serviceTokenSecretInput.value && serviceTokenSecretValid)) && + ((!syncIntegrationsInput.value && kibanaURLInputValid) || + (syncIntegrationsInput.value && + ((kibanaAPIKeyInput.value && kibanaAPIKeyValid) || + (kibanaAPIKeySecretInput.value && kibanaAPIKeySecretValid)) && + kibanaURLInputValid)) ); } else { // validate ES @@ -695,6 +737,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu caTrustedFingerprintInput, serviceTokenInput, serviceTokenSecretInput, + kibanaAPIKeyInput, + kibanaAPIKeySecretInput, + syncIntegrationsInput, + kibanaURLInput, sslCertificateInput, sslKeyInput, sslKeySecretInput, @@ -912,6 +958,18 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu ...shipperParams, } as NewLogstashOutput; case outputType.RemoteElasticsearch: + let secrets; + if (!serviceTokenInput.value && serviceTokenSecretInput.value) { + secrets = { + service_token: serviceTokenSecretInput.value, + }; + } + if (!kibanaAPIKeyInput.value && kibanaAPIKeySecretInput.value) { + secrets = { + ...(secrets ?? {}), + kibana_api_key: kibanaAPIKeySecretInput.value, + }; + } return { name: nameInput.value, type: outputType.RemoteElasticsearch, @@ -921,12 +979,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu preset: presetInput.value, config_yaml: additionalYamlConfigInput.value, service_token: serviceTokenInput.value || undefined, - ...(!serviceTokenInput.value && - serviceTokenSecretInput.value && { - secrets: { - service_token: serviceTokenSecretInput.value, - }, - }), + kibana_api_key: kibanaAPIKeyInput.value || undefined, + ...(secrets ? { secrets } : {}), + sync_integrations: syncIntegrationsInput.value, + kibana_url: kibanaURLInput.value || null, proxy_id: proxyIdValue, ...shipperParams, } as NewRemoteElasticsearchOutput; @@ -1036,6 +1092,10 @@ export function useOutputForm(onSucess: () => void, output?: Output, defaultOupu presetInput.value, serviceTokenInput.value, serviceTokenSecretInput.value, + kibanaAPIKeyInput.value, + kibanaAPIKeySecretInput.value, + syncIntegrationsInput.value, + kibanaURLInput.value, caTrustedFingerprintInput.value, confirm, notifications.toasts, diff --git a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts index 665cb9059654e..e2c63dd4c796a 100644 --- a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.test.ts @@ -258,4 +258,41 @@ describe('output handler', () => { expect(res).toEqual({ body: { item: { id: 'output1' } } }); }); + + it('should return error if both kibana_api_key and secrets.kibana_api_key is provided for remote_elasticsearch output', async () => { + jest + .spyOn(appContextService, 'getCloud') + .mockReturnValue({ isServerlessEnabled: false } as any); + + const res = await postOutputHandlerWithErrorHandler( + mockContext, + { + body: { + type: 'remote_elasticsearch', + kibana_api_key: 'value1', + secrets: { kibana_api_key: 'value2' }, + }, + } as any, + mockResponse as any + ); + + expect(res).toEqual({ + body: { message: 'Cannot specify both kibana_api_key and secrets.kibana_api_key' }, + statusCode: 400, + }); + }); + + it('should return ok if one of kibana_api_key and secrets.kibana_api_key is provided for remote_elasticsearch output', async () => { + jest + .spyOn(appContextService, 'getCloud') + .mockReturnValue({ isServerlessEnabled: false } as any); + + const res = await postOutputHandlerWithErrorHandler( + mockContext, + { body: { type: 'remote_elasticsearch', secrets: { kibana_api_key: 'value2' } } } as any, + mockResponse as any + ); + + expect(res).toEqual({ body: { item: { id: 'output1' } } }); + }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts index 0b33dd15e73fe..f760870424a9e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts +++ b/x-pack/platform/plugins/shared/fleet/server/routes/output/handler.ts @@ -44,12 +44,13 @@ function ensureNoDuplicateSecrets(output: Partial) { ) { throw Boom.badRequest('Cannot specify both ssl.key and secrets.ssl.key'); } - if ( - output.type === outputType.RemoteElasticsearch && - output.service_token && - output.secrets?.service_token - ) { - throw Boom.badRequest('Cannot specify both service_token and secrets.service_token'); + if (output.type === outputType.RemoteElasticsearch) { + if (output.service_token && output.secrets?.service_token) { + throw Boom.badRequest('Cannot specify both service_token and secrets.service_token'); + } + if (output.kibana_api_key && output.secrets?.kibana_api_key) { + throw Boom.badRequest('Cannot specify both kibana_api_key and secrets.kibana_api_key'); + } } } diff --git a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts index 94292f9a28bd5..fbbbd5d34137a 100644 --- a/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts +++ b/x-pack/platform/plugins/shared/fleet/server/saved_objects/index.ts @@ -402,6 +402,7 @@ export const getSavedObjectTypes = ( importableAndExportable: false, }, mappings: { + dynamic: false, properties: { output_id: { type: 'keyword', index: false }, name: { type: 'keyword' }, @@ -613,6 +614,14 @@ export const getSavedObjectTypes = ( }, ], }, + '8': { + changes: [ + { + type: 'mappings_addition', + addedMappings: {}, + }, + ], + }, }, migrations: { '7.13.0': migrateOutputToV7130, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap index 9d93fd5df3c81..5b17b992a56c8 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -309,8 +309,11 @@ Object { "hosts": Array [ "http://127.0.0.1:9201", ], + "kibana_api_key": undefined, + "kibana_url": undefined, "preset": "balanced", "service_token": undefined, + "sync_integrations": undefined, "type": "remote_elasticsearch", }, }, diff --git a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts index ea14167bb324a..c49752fd42f78 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/agent_policies/full_agent_policy.ts @@ -518,6 +518,9 @@ export function transformOutputToFullPolicyOutput( if (output.type === outputType.RemoteElasticsearch) { newOutput.service_token = output.service_token; + newOutput.kibana_api_key = output.kibana_api_key; + newOutput.kibana_url = output.kibana_url; + newOutput.sync_integrations = output.sync_integrations; } if (outputTypeSupportPresets(output.type)) { diff --git a/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts index addb48449e520..9d609e7e7d68d 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/output.test.ts @@ -2183,6 +2183,7 @@ describe('Output Service', () => { expect(soClient.update).toBeCalledWith(expect.anything(), expect.anything(), { type: 'remote_elasticsearch', + kibana_api_key: null, service_token: null, }); }); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/output.ts b/x-pack/platform/plugins/shared/fleet/server/services/output.ts index a33ef9fc9e9fc..06eb3102d8eec 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/output.ts @@ -696,6 +696,9 @@ class OutputService { if (!output.service_token && output.secrets?.service_token) { data.service_token = output.secrets?.service_token as string; } + if (!output.kibana_api_key && output.secrets?.kibana_api_key) { + data.kibana_api_key = output.secrets?.kibana_api_key as string; + } } } @@ -950,6 +953,7 @@ class OutputService { updateData.ca_trusted_fingerprint = null; updateData.ca_sha256 = null; delete (updateData as Nullable).service_token; + delete (updateData as Nullable).kibana_api_key; } if (data.type !== outputType.Logstash) { @@ -1072,6 +1076,9 @@ class OutputService { if (!data.service_token) { updateData.service_token = null; } + if (!data.kibana_api_key) { + updateData.kibana_api_key = null; + } } if (!data.preset && data.type === outputType.Elasticsearch) { @@ -1121,6 +1128,9 @@ class OutputService { if (!data.service_token && data.secrets?.service_token) { updateData.service_token = data.secrets?.service_token as string; } + if (!data.kibana_api_key && data.secrets?.kibana_api_key) { + updateData.kibana_api_key = data.secrets?.kibana_api_key as string; + } } } diff --git a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts index 35f1d29470919..e4aa34c0bc5c8 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.test.ts @@ -79,6 +79,7 @@ describe('output preconfiguration', () => { const keyHash = (await hashSecret('secretKey')) as string; const passwordHash = (await hashSecret('secretPassword')) as string; const serviceTokenHash = (await hashSecret('secretServiceToken')) as string; + const serviceKibanaAPIKeyHash = (await hashSecret('secretKibanaAPIKey')) as string; mockedOutputService.bulkGet.mockImplementation(async (soClient, id): Promise => { return [ { @@ -221,6 +222,7 @@ describe('output preconfiguration', () => { hosts: ['https:es.co:80'], is_preconfigured: true, service_token: 'unsecureServiceToken', + kibana_api_key: 'unsecureKibanaApiKey', }, { id: 'existing-remote-es-output-with-secrets-1', @@ -235,6 +237,10 @@ describe('output preconfiguration', () => { id: '101112', hash: serviceTokenHash, }, + kibana_api_key: { + id: '131415', + hash: serviceKibanaAPIKeyHash, + }, }, }, { @@ -247,6 +253,7 @@ describe('output preconfiguration', () => { is_preconfigured: true, secrets: { service_token: 'secretServiceToken', + kibana_api_key: 'secretKibanaAPIKey', }, }, ]; @@ -456,6 +463,9 @@ describe('output preconfiguration', () => { is_default_monitoring: false, hosts: ['test.fr'], service_token: 'serviceToken', + kibana_api_key: 'kibanaAPIKey', + kibana_url: 'http://kibana.co', + sync_integrations: true, } as PreconfiguredOutput, ]); @@ -809,6 +819,7 @@ describe('output preconfiguration', () => { is_preconfigured: true, secrets: { service_token: 'secretServiceToken', // no change + kibana_api_key: 'secretKibanaAPIKey', }, }, ]); @@ -898,6 +909,7 @@ describe('output preconfiguration', () => { hosts: ['https:es.co:80'], is_preconfigured: true, service_token: 'unsecureServiceToken', + kibana_api_key: 'unsecureKibanaAPIKey', } as PreconfiguredOutput, ]); @@ -969,6 +981,7 @@ describe('output preconfiguration', () => { is_preconfigured: true, secrets: { service_token: 'secretServiceToken', + kibana_api_key: 'secretKibanaAPIKey', }, }, ]); diff --git a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts index f605df4680b31..6070481bc16b7 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/preconfiguration/outputs.ts @@ -198,12 +198,21 @@ async function hashSecrets(output: PreconfiguredOutput) { } if (output.type === 'remote_elasticsearch') { const remoteESOutput = output as NewRemoteElasticsearchOutput; + let secrets; if (typeof remoteESOutput.secrets?.service_token === 'string') { const serviceToken = await hashSecret(remoteESOutput.secrets?.service_token); - return { + secrets = { service_token: serviceToken, }; } + if (typeof remoteESOutput.secrets?.kibana_api_key === 'string') { + const kibanaAPIKey = await hashSecret(remoteESOutput.secrets?.kibana_api_key); + secrets = { + ...(secrets ? secrets : {}), + kibana_api_key: kibanaAPIKey, + }; + } + return secrets; } return undefined; @@ -350,10 +359,17 @@ async function isPreconfiguredOutputDifferentFromCurrent( ) { return false; } - const serviceTokenIsDifferent = await isSecretDifferent( - preconfiguredOutput.secrets?.service_token, - existingOutput.secrets?.service_token - ); + const serviceTokenIsDifferent = + (await isSecretDifferent( + preconfiguredOutput.secrets?.service_token, + existingOutput.secrets?.service_token + )) || + (await isSecretDifferent( + preconfiguredOutput.secrets?.kibana_api_key, + existingOutput.secrets?.kibana_api_key + )) || + isDifferent(existingOutput.kibana_url, preconfiguredOutput.kibana_url) || + isDifferent(existingOutput.sync_integrations, preconfiguredOutput.sync_integrations); return serviceTokenIsDifferent; }; diff --git a/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts b/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts index 764340e1b5eb3..16f3b9d21c7eb 100644 --- a/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts +++ b/x-pack/platform/plugins/shared/fleet/server/services/secrets.ts @@ -333,6 +333,12 @@ function getOutputSecretPaths( value: remoteESOutput.secrets.service_token, }); } + if (remoteESOutput.secrets?.kibana_api_key) { + outputSecretPaths.push({ + path: 'secrets.kibana_api_key', + value: remoteESOutput.secrets.kibana_api_key, + }); + } } return outputSecretPaths; @@ -378,13 +384,17 @@ export function getOutputSecretReferences(output: Output): PolicySecretReference }); } - if ( - output.type === 'remote_elasticsearch' && - typeof output?.secrets?.service_token === 'object' - ) { - outputSecretPaths.push({ - id: output.secrets.service_token.id, - }); + if (output.type === 'remote_elasticsearch') { + if (typeof output?.secrets?.service_token === 'object') { + outputSecretPaths.push({ + id: output.secrets.service_token.id, + }); + } + if (typeof output?.secrets?.kibana_api_key === 'object') { + outputSecretPaths.push({ + id: output.secrets.kibana_api_key.id, + }); + } } return outputSecretPaths; diff --git a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts index 2f02819554d53..a8c85ea2c3ffe 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/models/output.ts @@ -152,8 +152,12 @@ export const RemoteElasticSearchSchema = { secrets: schema.maybe( schema.object({ service_token: schema.maybe(secretRefSchema), + kibana_api_key: schema.maybe(secretRefSchema), }) ), + sync_integrations: schema.maybe(schema.boolean()), + kibana_url: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + kibana_api_key: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), }; const RemoteElasticSearchUpdateSchema = { @@ -163,8 +167,12 @@ const RemoteElasticSearchUpdateSchema = { secrets: schema.maybe( schema.object({ service_token: schema.maybe(secretRefSchema), + kibana_api_key: schema.maybe(secretRefSchema), }) ), + sync_integrations: schema.maybe(schema.boolean()), + kibana_url: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), + kibana_api_key: schema.maybe(schema.oneOf([schema.literal(null), schema.string()])), }; /** diff --git a/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts b/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts index 7998e5da9b5e5..f04dc1b3c069e 100644 --- a/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts +++ b/x-pack/platform/plugins/shared/fleet/server/types/so_attributes.ts @@ -170,7 +170,11 @@ export interface OutputSoRemoteElasticsearchAttributes extends OutputSoBaseAttri service_token?: string; secrets?: { service_token?: { id: string }; + kibana_api_key?: { id: string }; }; + sync_integrations?: boolean; + kibana_url?: string; + kibana_api_key?: string; } interface OutputSoLogstashAttributes extends OutputSoBaseAttributes { diff --git a/x-pack/platform/plugins/shared/inference/README.md b/x-pack/platform/plugins/shared/inference/README.md index a52e589a9deab..fd90ac77b9b51 100644 --- a/x-pack/platform/plugins/shared/inference/README.md +++ b/x-pack/platform/plugins/shared/inference/README.md @@ -7,6 +7,27 @@ external LLM APIs. Its goals are: - Abstract away differences between different LLM providers like OpenAI, Bedrock and Gemini. - Allow us to move gradually to the \_inference endpoint without disrupting engineers. +## Usage with langchain + +The inference APIs are meant to be usable directly, and self-sufficient to power any RAG workflow. + +However, we're also exposing a way to use langchain while benefiting from the inference APIs, +via the `getChatModel` API exposed from the inference plugin's start contract. + +```ts +const chatModel = await inferenceStart.getChatModel({ + request, + connectorId: myInferenceConnectorId, + chatModelOptions: { + temperature: 0.2, + }, +}); + +// just use it as another langchain chatModel +``` + +Other langchain utilities are exposed from the `@kbn/inference-langchain` package. + ## Architecture and examples ![architecture-schema](https://github.com/user-attachments/assets/e65a3e47-bce1-4dcf-bbed-4f8ac12a104f) @@ -31,6 +52,7 @@ The list of inference connector types: - `.gen-ai`: OpenAI connector - `.bedrock`: Bedrock Claude connector - `.gemini`: Vertex Gemini connector +- `.inference`: Elastic Inference Endpoint connector ## Usage examples @@ -55,7 +77,7 @@ class MyPlugin { const inferenceClient = pluginsStart.inference.getClient({ request }); - const chatResponse = inferenceClient.chatComplete({ + const chatResponse = await inferenceClient.chatComplete({ connectorId: request.body.connectorId, system: `Here is my system message`, messages: [ @@ -91,7 +113,7 @@ const inferenceClient = myStartDeps.inference.getClient({ } }); -const chatResponse = inferenceClient.chatComplete({ +const chatResponse = await inferenceClient.chatComplete({ messages: [{ role: MessageRole.User, content: 'Do something' }], }); ``` @@ -113,7 +135,7 @@ In standard mode, the API returns a promise resolving with the full LLM response The response will also contain the token count info, if available. ```ts -const chatResponse = inferenceClient.chatComplete({ +const chatResponse = await inferenceClient.chatComplete({ connectorId: 'some-gen-ai-connector', system: `Here is my system message`, messages: [ @@ -188,7 +210,7 @@ The description and schema of a tool will be converted and sent to the LLM, so i to be explicit about what each tool does. ```ts -const chatResponse = inferenceClient.chatComplete({ +const chatResponse = await inferenceClient.chatComplete({ connectorId: 'some-gen-ai-connector', system: `Here is my system message`, messages: [ diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts index 88fa9a3a3c566..e4b91d493db1b 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/bedrock/bedrock_claude_adapter.ts @@ -15,6 +15,7 @@ import { } from '@kbn/inference-common'; import { parseSerdeChunkMessage } from './serde_utils'; import { InferenceConnectorAdapter } from '../../types'; +import { convertUpstreamError } from '../../utils'; import type { BedRockImagePart, BedRockMessage, BedRockTextPart } from './types'; import { BedrockChunkMember, @@ -57,8 +58,8 @@ export const bedrockClaudeAdapter: InferenceConnectorAdapter = { switchMap((response) => { if (response.status === 'error') { return throwError(() => - createInferenceInternalError(`Error calling connector: ${response.serviceMessage}`, { - rootError: response.serviceMessage, + convertUpstreamError(response.serviceMessage!, { + messagePrefix: 'Error calling connector:', }) ); } diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts index 792c95d6bb91c..a96b35f2bec84 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/gemini/gemini_adapter.ts @@ -18,6 +18,7 @@ import { ToolSchemaType, } from '@kbn/inference-common'; import type { InferenceConnectorAdapter } from '../../types'; +import { convertUpstreamError } from '../../utils'; import { eventSourceStreamIntoObservable } from '../../../util/event_source_stream_into_observable'; import { processVertexStream } from './process_vertex_stream'; import type { GenerateContentResponseChunk, GeminiMessage, GeminiToolConfig } from './types'; @@ -51,8 +52,8 @@ export const geminiAdapter: InferenceConnectorAdapter = { switchMap((response) => { if (response.status === 'error') { return throwError(() => - createInferenceInternalError(`Error calling connector: ${response.serviceMessage}`, { - rootError: response.serviceMessage, + convertUpstreamError(response.serviceMessage!, { + messagePrefix: 'Error calling connector:', }) ); } diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/inference/inference_adapter.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/inference/inference_adapter.ts index 5a00f8048c6a4..5e2a92ad90ace 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/inference/inference_adapter.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/inference/inference_adapter.ts @@ -9,7 +9,7 @@ import { from, identity, switchMap, throwError } from 'rxjs'; import { isReadable, Readable } from 'stream'; import { createInferenceInternalError } from '@kbn/inference-common'; import { eventSourceStreamIntoObservable } from '../../../util/event_source_stream_into_observable'; -import { isNativeFunctionCallingSupported } from '../../utils'; +import { convertUpstreamError, isNativeFunctionCallingSupported } from '../../utils'; import type { InferenceConnectorAdapter } from '../../types'; import { parseInlineFunctionCalls } from '../../simulated_function_calling'; import { processOpenAIStream, emitTokenCountEstimateIfMissing } from '../openai'; @@ -56,8 +56,8 @@ export const inferenceAdapter: InferenceConnectorAdapter = { switchMap((response) => { if (response.status === 'error') { return throwError(() => - createInferenceInternalError(`Error calling connector: ${response.serviceMessage}`, { - rootError: response.serviceMessage, + convertUpstreamError(response.serviceMessage!, { + messagePrefix: 'Error calling connector:', }) ); } diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.ts index 83b1a47131bbd..4cb4cf7d72cdc 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/openai_adapter.ts @@ -14,7 +14,7 @@ import { parseInlineFunctionCalls, wrapWithSimulatedFunctionCalling, } from '../../simulated_function_calling'; -import { isNativeFunctionCallingSupported } from '../../utils/function_calling_support'; +import { convertUpstreamError, isNativeFunctionCallingSupported } from '../../utils'; import type { OpenAIRequest } from './types'; import { messagesToOpenAI, toolsToOpenAI, toolChoiceToOpenAI } from './to_openai'; import { processOpenAIStream } from './process_openai_stream'; @@ -76,8 +76,8 @@ export const openAIAdapter: InferenceConnectorAdapter = { switchMap((response) => { if (response.status === 'error') { return throwError(() => - createInferenceInternalError(`Error calling connector: ${response.serviceMessage}`, { - rootError: response.serviceMessage, + convertUpstreamError(response.serviceMessage!, { + messagePrefix: 'Error calling connector:', }) ); } diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/stream_errors.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/stream_errors.ts index cef7a7ca49677..acd55244aa255 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/stream_errors.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/adapters/openai/stream_errors.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { createInferenceInternalError } from '@kbn/inference-common'; +import { convertUpstreamError } from '../../utils'; /** * Error line from standard openAI providers @@ -37,10 +37,10 @@ export type ErrorLine = OpenAIErrorLine | ElasticInferenceErrorLine | UnknownErr export const convertStreamError = ({ error }: ErrorLine) => { if ('message' in error) { - return createInferenceInternalError(error.message); + return convertUpstreamError(error.message); } else if ('reason' in error) { - return createInferenceInternalError(`${error.type} - ${error.reason}`); + return convertUpstreamError(`${error.type} - ${error.reason}`); } else { - return createInferenceInternalError(JSON.stringify(error)); + return convertUpstreamError(JSON.stringify(error)); } }; diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/convert_upstream_error.test.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/convert_upstream_error.test.ts new file mode 100644 index 0000000000000..78b40f6b34341 --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/convert_upstream_error.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { InferenceTaskErrorCode } from '@kbn/inference-common'; +import { convertUpstreamError } from './convert_upstream_error'; + +const connectorError = + "Status code: 400. Message: API Error: model_error - The response was filtered due to the prompt triggering Azure OpenAI's content management policy. Please modify your prompt and retry."; + +const elasticInferenceError = + 'status_exception - Received an authentication error status code for request from inference entity id [openai-chat_completion-uuid] status [401]. Error message: [Incorrect API key provided]'; + +describe('convertUpstreamError', () => { + it('extracts status code from a connector request error', () => { + const error = convertUpstreamError(connectorError); + expect(error.code).toEqual(InferenceTaskErrorCode.internalError); + expect(error.message).toEqual(connectorError); + expect(error.status).toEqual(400); + }); + + it('extracts status code from a ES inference chat_completion error', () => { + const error = convertUpstreamError(elasticInferenceError); + expect(error.code).toEqual(InferenceTaskErrorCode.internalError); + expect(error.message).toEqual(elasticInferenceError); + expect(error.status).toEqual(401); + }); + + it('supports errors', () => { + const error = convertUpstreamError(new Error(connectorError)); + expect(error.code).toEqual(InferenceTaskErrorCode.internalError); + expect(error.message).toEqual(connectorError); + expect(error.status).toEqual(400); + }); + + it('process generic messages', () => { + const message = 'some error message'; + const error = convertUpstreamError(message); + expect(error.code).toEqual(InferenceTaskErrorCode.internalError); + expect(error.message).toEqual(message); + expect(error.status).toBe(undefined); + }); +}); diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/convert_upstream_error.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/convert_upstream_error.ts new file mode 100644 index 0000000000000..47731eb300fcc --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/convert_upstream_error.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createInferenceInternalError, InferenceTaskInternalError } from '@kbn/inference-common'; + +const connectorStatusCodeRegexp = /Status code: ([0-9]{3})/i; +const inferenceStatusCodeRegexp = /status \[([0-9]{3})\]/i; + +export const convertUpstreamError = ( + source: string | Error, + { statusCode, messagePrefix }: { statusCode?: number; messagePrefix?: string } = {} +): InferenceTaskInternalError => { + const message = typeof source === 'string' ? source : source.message; + + let status = statusCode; + if (!status && typeof source === 'object') { + status = (source as any).status ?? (source as any).response?.status; + } + if (!status) { + const match = connectorStatusCodeRegexp.exec(message); + if (match) { + status = parseInt(match[1], 10); + } + } + if (!status) { + const match = inferenceStatusCodeRegexp.exec(message); + if (match) { + status = parseInt(match[1], 10); + } + } + + const messageWithPrefix = messagePrefix ? `${messagePrefix} ${message}` : message; + + return createInferenceInternalError(messageWithPrefix, { status }); +}; diff --git a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/index.ts b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/index.ts index 70322d0af00bd..71dbb7c9d639a 100644 --- a/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/index.ts +++ b/x-pack/platform/plugins/shared/inference/server/chat_complete/utils/index.ts @@ -16,3 +16,4 @@ export { streamToResponse } from './stream_to_response'; export { handleCancellation } from './handle_cancellation'; export { mergeChunks } from './merge_chunks'; export { isNativeFunctionCallingSupported } from './function_calling_support'; +export { convertUpstreamError } from './convert_upstream_error'; diff --git a/x-pack/platform/plugins/shared/inference/server/inference_client/create_chat_model.test.ts b/x-pack/platform/plugins/shared/inference/server/inference_client/create_chat_model.test.ts new file mode 100644 index 0000000000000..9a2d4ffa2562f --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/inference_client/create_chat_model.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright 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 { createChatModel } from './create_chat_model'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; +import { httpServerMock } from '@kbn/core/server/mocks'; +import { actionsMock } from '@kbn/actions-plugin/server/mocks'; + +jest.mock('./create_client'); +import { createClient } from './create_client'; +const createClientMock = createClient as unknown as jest.MockedFn; + +jest.mock('../util/get_connector_by_id'); +import { getConnectorById } from '../util/get_connector_by_id'; +const getConnectorByIdMock = getConnectorById as unknown as jest.MockedFn; + +jest.mock('@kbn/inference-langchain'); +import { InferenceChatModel } from '@kbn/inference-langchain'; +const InferenceChatModelMock = InferenceChatModel as unknown as jest.Mock< + typeof InferenceChatModel +>; + +describe('createChatModel', () => { + let logger: MockedLogger; + let actions: ReturnType; + let request: ReturnType; + + beforeEach(() => { + logger = loggerMock.create(); + actions = actionsMock.createStart(); + request = httpServerMock.createKibanaRequest(); + + createClientMock.mockReturnValue({ + chatComplete: jest.fn(), + } as any); + }); + + afterEach(() => { + createClientMock.mockReset(); + getConnectorByIdMock.mockReset(); + InferenceChatModelMock.mockReset(); + }); + + it('calls createClient with the right parameters', async () => { + await createChatModel({ + request, + connectorId: '.my-connector', + actions, + logger, + chatModelOptions: { + temperature: 0.3, + }, + }); + + expect(createClientMock).toHaveBeenCalledTimes(1); + expect(createClientMock).toHaveBeenCalledWith({ + actions, + request, + logger, + }); + }); + + it('calls getConnectorById with the right parameters', async () => { + const actionsClient = Symbol('actionsClient') as any; + actions.getActionsClientWithRequest.mockResolvedValue(actionsClient); + + await createChatModel({ + request, + connectorId: '.my-connector', + actions, + logger, + chatModelOptions: { + temperature: 0.3, + }, + }); + + expect(getConnectorById).toHaveBeenCalledTimes(1); + expect(getConnectorById).toHaveBeenCalledWith({ + connectorId: '.my-connector', + actionsClient, + }); + }); + + it('creates a InferenceChatModel with the right constructor params', async () => { + const inferenceClient = { + chatComplete: jest.fn(), + } as any; + createClientMock.mockReturnValue(inferenceClient); + + const connector = Symbol('connector') as any; + getConnectorByIdMock.mockResolvedValue(connector); + + await createChatModel({ + request, + connectorId: '.my-connector', + actions, + logger, + chatModelOptions: { + temperature: 0.3, + }, + }); + + expect(InferenceChatModelMock).toHaveBeenCalledTimes(1); + expect(InferenceChatModelMock).toHaveBeenCalledWith({ + chatComplete: inferenceClient.chatComplete, + connector, + logger, + temperature: 0.3, + }); + }); +}); diff --git a/x-pack/platform/plugins/shared/inference/server/inference_client/create_chat_model.ts b/x-pack/platform/plugins/shared/inference/server/inference_client/create_chat_model.ts new file mode 100644 index 0000000000000..a3956127889ea --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/inference_client/create_chat_model.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Logger } from '@kbn/logging'; +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { PluginStartContract as ActionsPluginStart } from '@kbn/actions-plugin/server'; +import { InferenceChatModel, type InferenceChatModelParams } from '@kbn/inference-langchain'; +import { getConnectorById } from '../util/get_connector_by_id'; +import { createClient } from './create_client'; + +export interface CreateChatModelOptions { + request: KibanaRequest; + connectorId: string; + actions: ActionsPluginStart; + logger: Logger; + chatModelOptions: Omit; +} + +export const createChatModel = async ({ + request, + connectorId, + actions, + logger, + chatModelOptions, +}: CreateChatModelOptions): Promise => { + const client = createClient({ + actions, + request, + logger, + }); + const actionsClient = await actions.getActionsClientWithRequest(request); + const connector = await getConnectorById({ connectorId, actionsClient }); + + return new InferenceChatModel({ + ...chatModelOptions, + chatComplete: client.chatComplete, + connector, + logger, + }); +}; diff --git a/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.test.ts b/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.test.ts index 98f5502cdfa55..bdd240a50b3d4 100644 --- a/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.test.ts +++ b/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.test.ts @@ -48,7 +48,11 @@ describe('createClient', () => { }); expect(createInferenceClientMock).toHaveBeenCalledTimes(1); - expect(createInferenceClientMock).toHaveBeenCalledWith({ request, actions, logger }); + expect(createInferenceClientMock).toHaveBeenCalledWith({ + request, + actions, + logger: logger.get('client'), + }); expect(bindClientMock).not.toHaveBeenCalled(); @@ -95,7 +99,7 @@ describe('createClient', () => { expect(createInferenceClientMock).toHaveBeenCalledWith({ request, actions, - logger, + logger: logger.get('client'), }); expect(bindClientMock).toHaveBeenCalledTimes(1); diff --git a/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.ts b/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.ts index 3507dd7fef8a8..ec57713bbbe5e 100644 --- a/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.ts +++ b/x-pack/platform/plugins/shared/inference/server/inference_client/create_client.ts @@ -29,7 +29,7 @@ export function createClient( options: UnboundOptions | BoundOptions ): BoundInferenceClient | InferenceClient { const { actions, request, logger } = options; - const client = createInferenceClient({ request, actions, logger }); + const client = createInferenceClient({ request, actions, logger: logger.get('client') }); if ('bindTo' in options) { return bindClient(client, options.bindTo); } else { diff --git a/x-pack/platform/plugins/shared/inference/server/inference_client/index.ts b/x-pack/platform/plugins/shared/inference/server/inference_client/index.ts index 9d56ebe7ff61a..a4decab7f117e 100644 --- a/x-pack/platform/plugins/shared/inference/server/inference_client/index.ts +++ b/x-pack/platform/plugins/shared/inference/server/inference_client/index.ts @@ -6,4 +6,5 @@ */ export { createClient } from './create_client'; +export { createChatModel } from './create_chat_model'; export type { InferenceClient, BoundInferenceClient } from './types'; diff --git a/x-pack/platform/plugins/shared/inference/server/mocks.ts b/x-pack/platform/plugins/shared/inference/server/mocks.ts new file mode 100644 index 0000000000000..6ee68257c6b7e --- /dev/null +++ b/x-pack/platform/plugins/shared/inference/server/mocks.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { InferenceServerStart } from './types'; + +const createStartContractMock = (): jest.Mocked => { + return { + getClient: jest.fn(), + getChatModel: jest.fn(), + }; +}; + +export const inferenceMock = { + createStartContract: createStartContractMock, +}; diff --git a/x-pack/platform/plugins/shared/inference/server/plugin.ts b/x-pack/platform/plugins/shared/inference/server/plugin.ts index 0f7090f483339..72d9f4e32ec5d 100644 --- a/x-pack/platform/plugins/shared/inference/server/plugin.ts +++ b/x-pack/platform/plugins/shared/inference/server/plugin.ts @@ -8,8 +8,9 @@ import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; import { - type BoundInferenceClient, createClient as createInferenceClient, + createChatModel, + type BoundInferenceClient, type InferenceClient, } from './inference_client'; import { registerRoutes } from './routes'; @@ -61,6 +62,16 @@ export class InferencePlugin logger: this.logger.get('client'), }) as T extends InferenceBoundClientCreateOptions ? BoundInferenceClient : InferenceClient; }, + + getChatModel: async (options) => { + return createChatModel({ + request: options.request, + connectorId: options.connectorId, + chatModelOptions: options.chatModelOptions, + actions: pluginsStart.actions, + logger: this.logger, + }); + }, }; } } diff --git a/x-pack/platform/plugins/shared/inference/server/routes/connectors.ts b/x-pack/platform/plugins/shared/inference/server/routes/connectors.ts index 285d1a1248e89..e28c56f241752 100644 --- a/x-pack/platform/plugins/shared/inference/server/routes/connectors.ts +++ b/x-pack/platform/plugins/shared/inference/server/routes/connectors.ts @@ -8,8 +8,8 @@ import type { CoreSetup, IRouter, RequestHandlerContext } from '@kbn/core/server'; import { InferenceConnector, - InferenceConnectorType, isSupportedConnectorType, + connectorToInference, } from '@kbn/inference-common'; import type { InferenceServerStart, InferenceStartDependencies } from '../types'; @@ -44,14 +44,7 @@ export function registerConnectorsRoute({ const connectors: InferenceConnector[] = allConnectors .filter((connector) => isSupportedConnectorType(connector.actionTypeId)) - .map((connector) => { - return { - connectorId: connector.id, - name: connector.name, - type: connector.actionTypeId as InferenceConnectorType, - config: connector.config ?? {}, - }; - }); + .map(connectorToInference); return response.ok({ body: { connectors } }); } diff --git a/x-pack/platform/plugins/shared/inference/server/types.ts b/x-pack/platform/plugins/shared/inference/server/types.ts index 8d6d1413f306a..c6cc72b1149eb 100644 --- a/x-pack/platform/plugins/shared/inference/server/types.ts +++ b/x-pack/platform/plugins/shared/inference/server/types.ts @@ -11,6 +11,7 @@ import type { } from '@kbn/actions-plugin/server'; import type { KibanaRequest } from '@kbn/core-http-server'; import type { BoundChatCompleteOptions } from '@kbn/inference-common'; +import type { InferenceChatModel, InferenceChatModelParams } from '@kbn/inference-langchain'; import type { InferenceClient, BoundInferenceClient } from './inference_client'; /* eslint-disable @typescript-eslint/no-empty-interface*/ @@ -93,4 +94,38 @@ export interface InferenceServerStart { getClient: ( options: T ) => T extends InferenceBoundClientCreateOptions ? BoundInferenceClient : InferenceClient; + + /** + * Creates a langchain {@link InferenceChatModel} that will be using the inference framework + * under the hood. + * + * @example + * ```ts + * const chatModel = await myStartDeps.inference.getChatModel({ + * request, + * connectorId: 'my-connector-id', + * chatModelOptions: { + * temperature: 0.3, + * } + * }); + */ + getChatModel: (options: CreateChatModelOptions) => Promise; +} + +/** + * Options to create an inference chat model using the {@link InferenceServerStart.getChatModel} API. + */ +export interface CreateChatModelOptions { + /** + * The request to scope the client to. + */ + request: KibanaRequest; + /** + * The id of the GenAI connector to use. + */ + connectorId: string; + /** + * Additional parameters to be passed down to the model constructor. + */ + chatModelOptions: Omit; } diff --git a/x-pack/platform/plugins/shared/inference/server/util/get_connector_by_id.ts b/x-pack/platform/plugins/shared/inference/server/util/get_connector_by_id.ts index 92846fc42640c..1b557c3236bb1 100644 --- a/x-pack/platform/plugins/shared/inference/server/util/get_connector_by_id.ts +++ b/x-pack/platform/plugins/shared/inference/server/util/get_connector_by_id.ts @@ -8,7 +8,7 @@ import type { ActionsClient, ActionResult as ActionConnector } from '@kbn/actions-plugin/server'; import { createInferenceRequestError, - isSupportedConnector, + connectorToInference, type InferenceConnector, } from '@kbn/inference-common'; @@ -32,17 +32,5 @@ export const getConnectorById = async ({ throw createInferenceRequestError(`No connector found for id '${connectorId}'`, 400); } - if (!isSupportedConnector(connector)) { - throw createInferenceRequestError( - `Connector '${connector.id}' of type '${connector.actionTypeId}' not recognized as a supported connector`, - 400 - ); - } - - return { - connectorId: connector.id, - name: connector.name, - type: connector.actionTypeId, - config: connector.config ?? {}, - }; + return connectorToInference(connector); }; diff --git a/x-pack/platform/plugins/shared/inference/tsconfig.json b/x-pack/platform/plugins/shared/inference/tsconfig.json index 1e012239f06cf..9c90ca45e34e7 100644 --- a/x-pack/platform/plugins/shared/inference/tsconfig.json +++ b/x-pack/platform/plugins/shared/inference/tsconfig.json @@ -36,5 +36,6 @@ "@kbn/es-types", "@kbn/field-types", "@kbn/expressions-plugin", + "@kbn/inference-langchain" ] } diff --git a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx index a5b9b1bb3bd97..9f264e07a5b6f 100644 --- a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx +++ b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx @@ -13,6 +13,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { SectionLoading, useKibana, attemptToURIDecode } from '../../../shared_imports'; import { PipelinesCreate } from '../pipelines_create'; +import { getErrorText } from '../utils'; export interface ParamProps { sourceName: string; @@ -36,7 +37,7 @@ export const PipelinesClone: FunctionComponent> useEffect(() => { if (error && !isLoading) { - services.notifications!.toasts.addError(error, { + services.notifications!.toasts.addError(new Error(getErrorText(error)), { title: i18n.translate('xpack.ingestPipelines.clone.loadSourcePipelineErrorTitle', { defaultMessage: 'Cannot load {name}.', values: { name: sourceName }, diff --git a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx index 2828d15e46315..a82b80825bcca 100644 --- a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx +++ b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx @@ -23,6 +23,7 @@ import { useKibana, SectionLoading, attemptToURIDecode } from '../../../shared_i import { getListPath } from '../../services/navigation'; import { PipelineForm } from '../../components'; import { useRedirectToPathOrRedirectPath } from '../../hooks'; +import { getErrorText } from '../utils'; interface MatchParams { name: string; @@ -136,7 +137,7 @@ export const PipelinesEdit: React.FunctionComponent } - body={

{error.message}

} + body={

{getErrorText(error)}

} actions={ { const { pipeline } = parse(location.search.substring(1)); @@ -114,7 +115,7 @@ export const PipelinesList: React.FunctionComponent = ({ /> } - body={

{error.message}

} + body={

{getErrorText(error)}

} actions={ { + it('it returns the message if present', () => { + expect( + getErrorText({ + error: 'Some error', + message: 'Some message', + }) + ).toBe('Some message'); + }); + + it('it returns the error if no message', () => { + expect( + getErrorText({ + error: 'Some error', + }) + ).toBe('Some error'); + }); + + it('it returns the error if message is empty', () => { + expect( + getErrorText({ + error: 'Some error', + message: '{}', + }) + ).toBe('Some error'); + }); +}); diff --git a/x-pack/test/functional_enterprise_search/services/index.ts b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/utils.ts similarity index 52% rename from x-pack/test/functional_enterprise_search/services/index.ts rename to x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/utils.ts index 6812b01e09473..ce3ecafbe219f 100644 --- a/x-pack/test/functional_enterprise_search/services/index.ts +++ b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/sections/utils.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { services as functionalServices } from '../../functional/services'; -import { AppSearchServiceProvider } from './app_search_service'; +import { Error } from '../../shared_imports'; -export const services = { - ...functionalServices, - appSearch: AppSearchServiceProvider, +export const getErrorText = (error: Error) => { + return error.message && error.message !== '{}' ? error.message : error.error; }; diff --git a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/services/api.ts b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/services/api.ts index e32245e325b15..7a3b357388f33 100644 --- a/x-pack/platform/plugins/shared/ingest_pipelines/public/application/services/api.ts +++ b/x-pack/platform/plugins/shared/ingest_pipelines/public/application/services/api.ts @@ -15,6 +15,7 @@ import { SendRequestResponse, sendRequest as _sendRequest, useRequest as _useRequest, + Error as _Error, } from '../../shared_imports'; import { UiMetricService } from './ui_metric'; import { @@ -29,7 +30,7 @@ export class ApiService { private client: HttpSetup | undefined; private uiMetricService: UiMetricService | undefined; - private useRequest(config: UseRequestConfig) { + private useRequest(config: UseRequestConfig) { if (!this.client) { throw new Error('Api service has not be initialized.'); } diff --git a/x-pack/platform/plugins/shared/lens/public/app_plugin/get_application_user_messages.tsx b/x-pack/platform/plugins/shared/lens/public/app_plugin/get_application_user_messages.tsx index b2755a411e719..e77fba29d731d 100644 --- a/x-pack/platform/plugins/shared/lens/public/app_plugin/get_application_user_messages.tsx +++ b/x-pack/platform/plugins/shared/lens/public/app_plugin/get_application_user_messages.tsx @@ -141,7 +141,7 @@ function getMissingIndexPatternsErrors( // Check for access to both Management app && specific indexPattern section const { management: isManagementEnabled } = core.application.capabilities.navLinks; const isIndexPatternManagementEnabled = - core.application.capabilities.management.kibana.indexPatterns; + core.application.capabilities.management?.kibana?.indexPatterns; const canFix = isManagementEnabled && isIndexPatternManagementEnabled; return [ { diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.test.ts b/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.test.ts index 7c2d546587c3e..a902d9bd35f8a 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.test.ts @@ -383,6 +383,8 @@ describe('Textbased Data Source', () => { { columnId: 'bytes', fieldName: 'bytes', + customLabel: false, + label: 'bytes', inMetricDimension: true, meta: { type: 'number', @@ -391,6 +393,8 @@ describe('Textbased Data Source', () => { { columnId: 'dest', fieldName: 'dest', + customLabel: false, + label: 'dest', meta: { type: 'string', }, @@ -471,6 +475,8 @@ describe('Textbased Data Source', () => { { id: '@timestamp', name: '@timestamp', + customLabel: false, + label: '@timestamp', meta: { type: 'date', }, @@ -478,6 +484,8 @@ describe('Textbased Data Source', () => { { id: 'dest', name: 'dest', + customLabel: false, + label: 'dest', meta: { type: 'string', }, @@ -517,6 +525,8 @@ describe('Textbased Data Source', () => { { columnId: '@timestamp', fieldName: '@timestamp', + customLabel: false, + label: '@timestamp', inMetricDimension: true, meta: { type: 'date', @@ -525,6 +535,8 @@ describe('Textbased Data Source', () => { { columnId: 'dest', fieldName: 'dest', + customLabel: false, + label: 'dest', inMetricDimension: true, meta: { type: 'string', diff --git a/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.tsx index 2fdcfe4eacee8..cc869f0c481df 100644 --- a/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/platform/plugins/shared/lens/public/datasources/text_based/text_based_languages.tsx @@ -235,8 +235,10 @@ export function getTextBasedDatasource({ ); return { columnId: c.variable ?? c.id, - fieldName: c.variable ? `?${c.variable}` : c.name, + fieldName: c.variable ? `?${c.variable}` : c.id, variable: c.variable, + label: c.name, + customLabel: c.id !== c.name, meta: c.meta, // makes non-number fields to act as metrics, used for datatable suggestions ...(inMetricDimension && { diff --git a/x-pack/platform/plugins/shared/lens/public/lens_suggestions_api/helpers.ts b/x-pack/platform/plugins/shared/lens/public/lens_suggestions_api/helpers.ts index 5e000d1f14c8a..f78989dc39470 100644 --- a/x-pack/platform/plugins/shared/lens/public/lens_suggestions_api/helpers.ts +++ b/x-pack/platform/plugins/shared/lens/public/lens_suggestions_api/helpers.ts @@ -50,7 +50,7 @@ export function mergeSuggestionWithVisContext({ (layer) => layer.columns?.some( (c: { fieldName: string }) => - !context?.textBasedColumns?.find((col) => col.name === c.fieldName) + !context?.textBasedColumns?.find((col) => col.id === c.fieldName) ) || layer.columns?.length !== context?.textBasedColumns?.length ) ) { diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts index 57bdf3889397e..21b06ed8b5512 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.test.ts @@ -34,7 +34,7 @@ import { } from '@kbn/presentation-publishing'; import { PublishesSearchSession } from '@kbn/presentation-publishing/interfaces/fetch/publishes_search_session'; import { isObject } from 'lodash'; -import { defaultDoc } from '../mocks'; +import { createMockDatasource, defaultDoc } from '../mocks'; jest.mock('@kbn/interpreter', () => ({ toExpression: jest.fn().mockReturnValue('expression'), @@ -87,13 +87,21 @@ type ChangeFnType = ({ async function expectRerenderOnDataLoder( changeFn: ChangeFnType, runtimeState: LensRuntimeState = { attributes: getLensAttributesMock() }, - parentApiOverrides?: Partial< - { - filters$: BehaviorSubject; - query$: BehaviorSubject; - timeRange$: BehaviorSubject; - } & LensOverrides - > + { + parentApiOverrides, + servicesOverrides, + internalApiOverrides, + }: { + parentApiOverrides?: Partial< + { + filters$: BehaviorSubject; + query$: BehaviorSubject; + timeRange$: BehaviorSubject; + } & LensOverrides + >; + internalApiOverrides?: Partial; + servicesOverrides?: Partial; + } = {} ): Promise { const parentApi = { ...createUnifiedSearchApi(), @@ -116,12 +124,15 @@ async function expectRerenderOnDataLoder( parentApi, }; const getState = jest.fn(() => runtimeState); - const internalApi = getLensInternalApiMock(); - const services = makeEmbeddableServices(new BehaviorSubject(''), undefined, { - visOverrides: { id: 'lnsXY' }, - dataOverrides: { id: 'form_based' }, - }); - services.documentToExpression = jest.fn().mockResolvedValue({ ast: 'expression_string' }); + const internalApi = getLensInternalApiMock(internalApiOverrides); + const services = { + ...makeEmbeddableServices(new BehaviorSubject(''), undefined, { + visOverrides: { id: 'lnsXY' }, + dataOverrides: { id: 'form_based' }, + }), + documentToExpression: jest.fn().mockResolvedValue({ ast: 'expression_string' }), + ...servicesOverrides, + }; const { cleanup } = loadEmbeddableData( faker.string.uuid(), getState, @@ -242,7 +253,7 @@ describe('Data Loader', () => { return false; }, undefined, // use default attributes - createUnifiedSearchApi(query, filters) // customize parentApi + { parentApiOverrides: createUnifiedSearchApi(query, filters) } // customize parentApi ); }); @@ -305,7 +316,13 @@ describe('Data Loader', () => { return false; }, { attributes }, - createUnifiedSearchApi(parentApiQuery, parentApiFilters, parentApiTimeRange) + { + parentApiOverrides: createUnifiedSearchApi( + parentApiQuery, + parentApiFilters, + parentApiTimeRange + ), + } ); }); @@ -411,4 +428,54 @@ describe('Data Loader', () => { } ); }); + + it('should catch missing dataView errors correctly', async () => { + await expectRerenderOnDataLoder( + async ({ internalApi }) => { + // wait for the error to appear + await waitForValue(internalApi.blockingError$); + + const error = internalApi.blockingError$.getValue()!; + expect(error.message).toEqual( + 'Could not find the data view: 90943e30-9a47-11e8-b64d-95841ca0b247' + ); + return false; + }, + undefined, + // Unfortuantely some mocks are required here to make the test work + { + // Mock the testing datasource to return an error when asked for checkIntegrity + servicesOverrides: { + datasourceMap: { + form_based: { + ...createMockDatasource('form_based'), + checkIntegrity: jest.fn().mockReturnValue(['90943e30-9a47-11e8-b64d-95841ca0b247']), + }, + }, + }, + // Mock the visualization context to fully load the datasource state + internalApiOverrides: { + getVisualizationContext: jest.fn().mockReturnValue({ + activeAttributes: { + ...defaultDoc, + visualizationType: 'lnsXY', + state: { ...defaultDoc.state, datasourceStates: { form_based: {} } }, + }, + mergedSearchContext: { + now: Date.now(), + timeRange: { from: 'now-15m', to: 'now' }, + query: [defaultDoc.state.query], + filters: [], + disableWarningToasts: true, + }, + indexPatterns: [], + indexPatternRefs: {}, + activeVisualizationState: { activeId: 'lnsXY' }, + activeDatasourceState: {}, + activeData: {}, + }), + }, + } + ); + }); }); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts index 4bc24c514c534..952e41112369d 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/data_loader.ts @@ -6,10 +6,9 @@ */ import type { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; -import { apiPublishesESQLVariables } from '@kbn/esql-variables-types'; import { apiPublishesUnifiedSearch, fetch$ } from '@kbn/presentation-publishing'; +import type { ESQLControlVariable } from '@kbn/esql-validation-autocomplete'; import { type KibanaExecutionContext } from '@kbn/core/public'; -import { ESQLControlVariable } from '@kbn/esql-validation-autocomplete'; import { BehaviorSubject, type Subscription, @@ -20,7 +19,6 @@ import { merge, tap, map, - of, } from 'rxjs'; import fastIsEqual from 'fast-deep-equal'; import { pick } from 'lodash'; @@ -202,7 +200,7 @@ export function loadEmbeddableData( ); // Go concurrently: build the expression and fetch the dataViews - const [{ params, abortController, ...rest }, dataViews] = await Promise.all([ + const [{ params, abortController, ...rest }, dataViewIds] = await Promise.all([ getExpressionRendererParams(currentState, { searchContext, api, @@ -242,9 +240,12 @@ export function loadEmbeddableData( }); // Publish the used dataViews on the Lens API - internalApi.updateDataViews(dataViews); + internalApi.updateDataViews(dataViewIds); - if (params?.expression != null && !dispatchBlockingErrorIfAny()) { + // This will catch also failed loaded dataViews + const hasBlockingErrors = dispatchBlockingErrorIfAny(); + + if (params?.expression != null && !hasBlockingErrors) { internalApi.updateExpressionParams(params); } @@ -256,15 +257,13 @@ export function loadEmbeddableData( return pipe(distinctUntilChanged(fastIsEqual), skip(1)); } - // Read ESQL variables from the parent if it provides them - const esqlVariables$ = apiPublishesESQLVariables(parentApi) - ? parentApi.esqlVariables$ - : of([] as ESQLControlVariable[]); - const mergedSubscriptions = merge( // on search context change, reload fetch$(api).pipe(map(() => 'searchContext' as ReloadReason)), - esqlVariables$.pipe(map(() => 'ESQLvariables' as ReloadReason)), + internalApi?.esqlVariables$.pipe( + waitUntilChanged(), + map(() => 'ESQLvariables' as ReloadReason) + ), // On state change, reload // this is used to refresh the chart on inline editing // just make sure to avoid to rerender if there's no substantial change diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts index 7b2cb38b36e87..34e5f43d66ec2 100644 --- a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/initialize_internal_api.ts @@ -8,6 +8,7 @@ import { BehaviorSubject } from 'rxjs'; import { initializeTitleManager } from '@kbn/presentation-publishing'; import { apiPublishesESQLVariables } from '@kbn/esql-variables-types'; +import type { ESQLControlVariable } from '@kbn/esql-validation-autocomplete'; import type { DataView } from '@kbn/data-views-plugin/common'; import { buildObservableVariable, createEmptyLensState } from '../helper'; import type { @@ -20,6 +21,7 @@ import type { } from '../types'; import { apiHasAbortController, apiHasLensComponentProps } from '../type_guards'; import type { UserMessage } from '../../types'; +import { getEmbeddableVariables } from './utils'; export function initializeInternalApi( initialState: LensRuntimeState, @@ -71,13 +73,21 @@ export function initializeInternalApi( apiPublishesESQLVariables(parentApi) ? parentApi.esqlVariables$ : [] ); + const query = initialState.attributes.state.query; + + const panelEsqlVariables$ = new BehaviorSubject([]); + esqlVariables$.subscribe((newVariables) => { + const esqlVariables = getEmbeddableVariables(query, newVariables) ?? []; + panelEsqlVariables$.next(esqlVariables); + }); + // No need to expose anything at public API right now, that would happen later on // where each initializer will pick what it needs and publish it return { attributes$, overrides$, disableTriggers$, - esqlVariables$, + esqlVariables$: panelEsqlVariables$, dataLoading$, hasRenderCompleted$, expressionParams$, diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/utils.test.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/utils.test.ts new file mode 100644 index 0000000000000..a058e16b49873 --- /dev/null +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/utils.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { ESQLVariableType } from '@kbn/esql-validation-autocomplete'; +import { getEmbeddableVariables } from './utils'; + +describe('getEmbeddableVariables', () => { + test('should return undefined if query is not of type AggregateQuery', () => { + const query = { + language: 'kuery', + query: 'test', + }; + const esqlVariables = [ + { + key: 'field', + value: 'test', + type: ESQLVariableType.FIELDS, + }, + { + key: 'interval', + value: '1 hour', + type: ESQLVariableType.TIME_LITERAL, + }, + { + key: 'value', + value: 'meow', + type: ESQLVariableType.VALUES, + }, + ]; + expect(getEmbeddableVariables(query, esqlVariables)).toBeUndefined(); + }); + + test('should return esqlVariables if query has no variables', () => { + const query = { + esql: 'FROM index', + }; + const esqlVariables = [ + { + key: 'field', + value: 'test', + type: ESQLVariableType.FIELDS, + }, + { + key: 'interval', + value: '1 hour', + type: ESQLVariableType.TIME_LITERAL, + }, + { + key: 'value', + value: 'meow', + type: ESQLVariableType.VALUES, + }, + ]; + expect(getEmbeddableVariables(query, esqlVariables)).toEqual(esqlVariables); + }); + + test('should return only the associated variables with the query', () => { + const query = { + esql: 'FROM index | STATS COUNT(*) BY ?field', + }; + const esqlVariables = [ + { + key: 'field', + value: 'test', + type: ESQLVariableType.FIELDS, + }, + { + key: 'interval', + value: '1 hour', + type: ESQLVariableType.TIME_LITERAL, + }, + { + key: 'value', + value: 'meow', + type: ESQLVariableType.VALUES, + }, + ]; + expect(getEmbeddableVariables(query, esqlVariables)).toEqual([ + { + key: 'field', + value: 'test', + type: ESQLVariableType.FIELDS, + }, + ]); + }); +}); diff --git a/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/utils.ts b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/utils.ts new file mode 100644 index 0000000000000..c054c8dd67c93 --- /dev/null +++ b/x-pack/platform/plugins/shared/lens/public/react_embeddable/initializers/utils.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { type AggregateQuery, type Query, isOfAggregateQueryType } from '@kbn/es-query'; +import type { ESQLControlVariable } from '@kbn/esql-validation-autocomplete'; +import { getESQLQueryVariables } from '@kbn/esql-utils'; + +export function getEmbeddableVariables( + query: Query | AggregateQuery, + esqlVariables: ESQLControlVariable[] +) { + if (isOfAggregateQueryType(query)) { + const currentVariables = getESQLQueryVariables(query.esql); + if (!currentVariables.length) { + return esqlVariables; + } + // filter out the variables that are not used in the query + return esqlVariables.filter((variable) => currentVariables.includes(variable.key)); + } +} diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx index 90c644555d6f6..8fd8b98406e31 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/dimension_editor.tsx @@ -115,7 +115,7 @@ export function TableDimensionEditor(props: TableDimensionEditorProps) { }; // need to tell the helper that the colorStops are required to display const displayStops = applyPaletteParams(props.paletteService, activePalette, currentMinMax); - const categories = getColorCategories(currentData?.rows ?? [], accessor, false, [null]); + const categories = getColorCategories(currentData?.rows, accessor, [null]); if (activePalette.name !== CUSTOM_PALETTE && activePalette.params?.stops) { activePalette.params.stops = applyPaletteParams( diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss index 117c6797c3620..0f0ed53a10021 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.scss @@ -1,10 +1,5 @@ .lnsDataTableContainer { height: 100%; - - // EUI issue to make background transparent https://github.com/elastic/eui/issues/8136 - .euiDataGrid__content { - background: transparent; - } } .lnsTableCell--multiline { diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx index dc6f818eb3519..3bbacf124668f 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/datatable/components/table_basic.tsx @@ -32,7 +32,7 @@ import { ClickTriggerEvent } from '@kbn/charts-plugin/public'; import { IconChartDatatable } from '@kbn/chart-icons'; import useObservable from 'react-use/lib/useObservable'; import { getColorCategories } from '@kbn/chart-expressions-common'; -import { getOriginalId, isTransposeId } from '@kbn/transpose-utils'; +import { getOriginalId } from '@kbn/transpose-utils'; import { CoreTheme } from '@kbn/core/public'; import { getKbnPalettes } from '@kbn/palettes'; import type { LensTableRowContextMenuEvent } from '../../../types'; @@ -405,16 +405,12 @@ export const DatatableComponent = (props: DatatableRenderProps) => { const dataType = getFieldMetaFromDatatable(firstLocalTable, originalId)?.type; const isBucketed = bucketedColumns.some((id) => id === columnId); const colorByTerms = shouldColorByTerms(dataType, isBucketed); - + const categoryRows = (untransposedDataRef.current ?? firstLocalTable)?.rows; const data: ColorMappingInputData = colorByTerms ? { type: 'categories', - categories: getColorCategories( - firstLocalTable.rows, - originalId, - isTransposeId(columnId), - [null] - ), + // Must use non-transposed data here to correctly collate categories across transposed columns + categories: getColorCategories(categoryRows, originalId, [null]), } : { type: 'ranges', diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx index 21cd550d4f53d..113c4f0d9e015 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/partition/dimension_editor.tsx @@ -130,7 +130,7 @@ export function DimensionEditor(props: DimensionEditorProps) { currentLayer.colorMapping ); const table = props.frame.activeData?.[currentLayer.layerId]; - const splitCategories = getColorCategories(table?.rows ?? [], props.accessor); + const splitCategories = getColorCategories(table?.rows, props.accessor); return ( <> diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx index 5dea1057c3712..2e9b451015a4f 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/tagcloud/tags_dimension_editor.tsx @@ -65,7 +65,7 @@ export function TagsDimensionEditor({ state.colorMapping ); const table = frame.activeData?.[state.layerId]; - const splitCategories = getColorCategories(table?.rows ?? [], state.tagAccessor); + const splitCategories = getColorCategories(table?.rows, state.tagAccessor); const setColorMapping = useCallback( (colorMapping?: ColorMapping.Config) => { diff --git a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx index 994e254bea954..fd31ffcfd1245 100644 --- a/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx +++ b/x-pack/platform/plugins/shared/lens/public/visualizations/xy/xy_config_panel/dimension_editor.tsx @@ -134,7 +134,7 @@ export function DataDimensionEditor( const table = props.frame.activeData?.[layer.layerId]; const { splitAccessor } = layer; - const splitCategories = getColorCategories(table?.rows ?? [], splitAccessor); + const splitCategories = getColorCategories(table?.rows, splitAccessor); if (props.groupId === 'breakdown') { return !layer.collapseFn ? ( diff --git a/x-pack/platform/plugins/shared/observability_ai_assistant/server/plugin.ts b/x-pack/platform/plugins/shared/observability_ai_assistant/server/plugin.ts index 564cabf3ed1f5..bf61ed450f8d7 100644 --- a/x-pack/platform/plugins/shared/observability_ai_assistant/server/plugin.ts +++ b/x-pack/platform/plugins/shared/observability_ai_assistant/server/plugin.ts @@ -15,6 +15,7 @@ import { import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; import { KibanaFeatureScope } from '@kbn/features-plugin/common'; +import { ApiPrivileges } from '@kbn/security-authorization-core-common'; import { OBSERVABILITY_AI_ASSISTANT_FEATURE_ID } from '../common/feature'; import type { ObservabilityAIAssistantConfig } from './config'; import { registerServerRoutes } from './routes/register_routes'; @@ -72,7 +73,11 @@ export class ObservabilityAIAssistantPlugin privileges: { all: { app: [OBSERVABILITY_AI_ASSISTANT_FEATURE_ID, 'kibana'], - api: [OBSERVABILITY_AI_ASSISTANT_FEATURE_ID, 'ai_assistant', 'manage_llm_product_doc'], + api: [ + OBSERVABILITY_AI_ASSISTANT_FEATURE_ID, + 'ai_assistant', + ApiPrivileges.manage('llm_product_doc'), + ], catalogue: [OBSERVABILITY_AI_ASSISTANT_FEATURE_ID], savedObject: { all: [], diff --git a/x-pack/platform/plugins/shared/observability_ai_assistant/tsconfig.json b/x-pack/platform/plugins/shared/observability_ai_assistant/tsconfig.json index 823bc57019294..a43de61a375e0 100644 --- a/x-pack/platform/plugins/shared/observability_ai_assistant/tsconfig.json +++ b/x-pack/platform/plugins/shared/observability_ai_assistant/tsconfig.json @@ -50,7 +50,8 @@ "@kbn/core-lifecycle-server", "@kbn/server-route-repository-utils", "@kbn/inference-plugin", - "@kbn/ai-assistant-icon" + "@kbn/ai-assistant-icon", + "@kbn/security-authorization-core-common" ], "exclude": ["target/**/*"] } diff --git a/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx b/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx index 63c395b1f4bbc..6d9c8354bc0df 100644 --- a/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx +++ b/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.test.tsx @@ -437,6 +437,72 @@ describe('EditRoleMappingPage', () => { expect(rulePanels.at(0).props().readOnly).toBeTruthy(); }); + it('renders a readonly view when role mapping has metadata._readonly=true', async () => { + const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); + roleMappingsAPI.saveRoleMapping.mockResolvedValue(null); + roleMappingsAPI.getRoleMapping.mockResolvedValue({ + name: 'foo', + role_templates: [ + { + template: { id: 'foo' }, + }, + ], + enabled: true, + rules: { + all: [ + { + field: { + username: '*', + }, + }, + { + all: [], + }, + ], + }, + metadata: { + _read_only: true, + }, + }); + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, + hasCompatibleRealms: true, + canUseInlineScripts: true, + canUseStoredScripts: true, + }); + + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI, 'foo'); + await nextTick(); + wrapper.update(); + + // back button + const backButton = wrapper.find('button[data-test-subj="roleMappingFormReturnButton"]'); + expect(backButton).toHaveLength(1); + + // no save button + const saveButton = wrapper.find('button[data-test-subj="saveRoleMappingButton"]'); + expect(saveButton).toHaveLength(0); + + // no delete button + const deleteButton = wrapper.find('emptyButton[data-test-subj="deleteRoleMappingButton"]'); + expect(deleteButton).toHaveLength(0); + + // Info panel is read-only (view mode) + const infoPanels = wrapper.find('MappingInfoPanel[data-test-subj="roleMappingInfoPanel"]'); + expect(infoPanels).toHaveLength(1); + expect(infoPanels.at(0).props().mode).toEqual('view'); + + // Rule panel is read-only + const rulePanels = wrapper.find('RuleEditorPanel[data-test-subj="roleMappingRulePanel"]'); + expect(rulePanels).toHaveLength(1); + expect(rulePanels.at(0).props().readOnly).toBeTruthy(); + + // Lock icon is displayed + const lockIcon = wrapper.find('EuiToolTip[data-test-subj="readOnlyRoleMappingTooltip"]'); + expect(lockIcon).toHaveLength(1); + }); + it('renders a warning when empty any or all rules are present', async () => { const roleMappingsAPI = roleMappingsAPIClientMock.create(); const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); diff --git a/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx b/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx index f9f1160bcca79..86eca799ac771 100644 --- a/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx +++ b/x-pack/platform/plugins/shared/security/public/management/role_mappings/edit_role_mapping/edit_role_mapping_page.tsx @@ -12,10 +12,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiForm, + EuiIcon, EuiLink, EuiPageHeader, EuiPageSection, EuiSpacer, + EuiToolTip, } from '@elastic/eui'; import React, { Component } from 'react'; @@ -179,7 +181,7 @@ export class EditRoleMappingPage extends Component { }) } docLinks={this.props.docLinks} - readOnly={this.props.readOnly} + readOnly={this.isReadOnly()} /> {this.getFormWarnings()} @@ -191,16 +193,32 @@ export class EditRoleMappingPage extends Component { } private getInfoPanelMode = () => { - return this.props.readOnly ? 'view' : this.editingExistingRoleMapping() ? 'edit' : 'create'; + return this.isReadOnly() ? 'view' : this.editingExistingRoleMapping() ? 'edit' : 'create'; }; private getFormTitle = () => { - if (this.props.readOnly) { + if (this.isReadOnly()) { return ( - + <> + +   + {this.isReadOnlyRoleMapping() && ( + + } + > + + + )} + ); } if (this.editingExistingRoleMapping()) { @@ -279,7 +297,7 @@ export class EditRoleMappingPage extends Component { }; private getFormButtons = () => { - if (this.props.readOnly === true) { + if (this.isReadOnly() === true) { return this.getReturnToRoleMappingListButton(); } @@ -338,7 +356,7 @@ export class EditRoleMappingPage extends Component { }; private getDeleteButton = () => { - if (this.editingExistingRoleMapping() && !this.props.readOnly) { + if (this.editingExistingRoleMapping() && !this.isReadOnly()) { return ( { private cloningExistingRoleMapping = () => typeof this.props.name === 'string' && this.props.action === 'clone'; + private isReadOnlyRoleMapping = () => this.state.roleMapping?.metadata?._read_only; + + private isReadOnly = () => this.props.readOnly || this.isReadOnlyRoleMapping(); + private async loadAppData() { try { const [features, roleMapping] = await Promise.all([ diff --git a/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx b/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx index a9d770c50a1dd..d544f74195c27 100644 --- a/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx +++ b/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.test.tsx @@ -396,5 +396,46 @@ describe('RoleMappingsGridPage', () => { const actionMenuButton = wrapper.find('[data-test-subj="euiCollapsedItemActionsButton"]'); expect(actionMenuButton).toHaveLength(0); }); + + it('hides controls when role mapping is read only', async () => { + const roleMappingsAPI = roleMappingsAPIClientMock.create(); + const securityFeaturesAPI = securityFeaturesAPIClientMock.create(); + roleMappingsAPI.getRoleMappings.mockResolvedValue([ + { + name: 'some-realm', + enabled: true, + roles: ['superuser'], + metadata: { _read_only: true }, + rules: { field: { username: '*' } }, + }, + ]); + securityFeaturesAPI.checkFeatures.mockResolvedValue({ + canReadSecurity: true, + hasCompatibleRealms: true, + }); + roleMappingsAPI.deleteRoleMappings.mockResolvedValue([ + { + name: 'some-realm', + success: true, + }, + ]); + + const wrapper = renderView(roleMappingsAPI, securityFeaturesAPI); + await nextTick(); + wrapper.update(); + + // role mapping actions are hidden + const editButton = wrapper.find('[data-test-subj="editRoleMappingButton-some-realm"]'); + expect(editButton).toHaveLength(0); + + const cloneButton = wrapper.find('[data-test-subj="cloneRoleMappingButton-some-realm"]'); + expect(cloneButton).toHaveLength(0); + + const deleteButton = wrapper.find('[data-test-subj="deleteRoleMappingButton-some-realm"]'); + expect(deleteButton).toHaveLength(0); + + const actionMenuButton = wrapper.find('[data-test-subj="euiCollapsedItemActionsButton"]'); + expect(actionMenuButton).toHaveLength(0); + }); }); }); diff --git a/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx b/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx index 76bd3117d501a..8bfba2f1eee6b 100644 --- a/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx +++ b/x-pack/platform/plugins/shared/security/public/management/role_mappings/role_mappings_grid/role_mappings_grid_page.tsx @@ -194,6 +194,8 @@ export class RoleMappingsGridPage extends Component { ); } + private isReadOnlyRoleMapping = (record: RoleMapping) => record.metadata?._read_only; + private renderTable = () => { const { roleMappings, selectedItems, loadState } = this.state; @@ -283,7 +285,15 @@ export class RoleMappingsGridPage extends Component { columns={this.getColumnConfig(deleteRoleMappingPrompt)} search={search} sorting={sorting} - selection={this.props.readOnly ? undefined : selection} + selection={ + this.props.readOnly + ? undefined + : { + selectable: (roleMapping: RoleMapping) => + !this.isReadOnlyRoleMapping(roleMapping), + ...selection, + } + } pagination={pagination} loading={loadState === 'loadingTable'} message={message} @@ -386,6 +396,7 @@ export class RoleMappingsGridPage extends Component { name: i18n.translate('xpack.security.management.roleMappings.actionCloneTooltip', { defaultMessage: 'Clone', }), + available: (roleMapping: RoleMapping) => !this.isReadOnlyRoleMapping(roleMapping), description: (record: RoleMapping) => i18n.translate('xpack.security.management.roleMappings.actionCloneAriaLabel', { defaultMessage: `Clone ''{name}''`, @@ -406,6 +417,7 @@ export class RoleMappingsGridPage extends Component { name: i18n.translate('xpack.security.management.roleMappings.actionDeleteTooltip', { defaultMessage: 'Delete', }), + available: (roleMapping: RoleMapping) => !this.isReadOnlyRoleMapping(roleMapping), description: (record: RoleMapping) => i18n.translate('xpack.security.management.roleMappings.actionDeleteAriaLabel', { defaultMessage: `Delete ''{name}''`, @@ -422,6 +434,7 @@ export class RoleMappingsGridPage extends Component { name: i18n.translate('xpack.security.management.roleMappings.actionEditTooltip', { defaultMessage: 'Edit', }), + available: (roleMapping: RoleMapping) => !this.isReadOnlyRoleMapping(roleMapping), description: (record: RoleMapping) => i18n.translate('xpack.security.management.roleMappings.actionEditAriaLabel', { defaultMessage: `Edit ''{name}''`, diff --git a/x-pack/platform/plugins/shared/serverless/common/index.ts b/x-pack/platform/plugins/shared/serverless/common/index.ts index dacf50415f2a7..f28f6aa0c3623 100644 --- a/x-pack/platform/plugins/shared/serverless/common/index.ts +++ b/x-pack/platform/plugins/shared/serverless/common/index.ts @@ -7,6 +7,3 @@ export const PLUGIN_ID = 'serverless'; export const PLUGIN_NAME = 'serverless'; - -/** Internal API route responsible for switching between project configurations. */ -export const API_SWITCH_PROJECT = '/internal/serverless/switch_project'; diff --git a/x-pack/platform/plugins/shared/serverless/public/index.ts b/x-pack/platform/plugins/shared/serverless/public/index.ts index 7f841ef25be2f..d47e125a14ab6 100644 --- a/x-pack/platform/plugins/shared/serverless/public/index.ts +++ b/x-pack/platform/plugins/shared/serverless/public/index.ts @@ -5,11 +5,10 @@ * 2.0. */ -import { PluginInitializerContext } from '@kbn/core/public'; import { ServerlessPlugin } from './plugin'; -export function plugin(initializerContext: PluginInitializerContext) { - return new ServerlessPlugin(initializerContext); +export function plugin() { + return new ServerlessPlugin(); } export type { ServerlessPluginSetup, ServerlessPluginStart } from './types'; diff --git a/x-pack/platform/plugins/shared/serverless/public/plugin.tsx b/x-pack/platform/plugins/shared/serverless/public/plugin.tsx index a488658e9bb94..139bce182318d 100644 --- a/x-pack/platform/plugins/shared/serverless/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/serverless/public/plugin.tsx @@ -7,16 +7,11 @@ import { EuiButton } from '@elastic/eui'; import { InternalChromeStart } from '@kbn/core-chrome-browser-internal'; -import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; +import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { toMountPoint } from '@kbn/react-kibana-mount'; import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import { ProjectSwitcher, ProjectSwitcherKibanaProvider } from '@kbn/serverless-project-switcher'; -import { ProjectType } from '@kbn/serverless-types'; import React from 'react'; -import ReactDOM from 'react-dom'; -import { API_SWITCH_PROJECT as projectChangeAPIUrl } from '../common'; -import { ServerlessConfig } from './config'; import { generateManageOrgMembersNavCard, manageOrgMembersNavCardName, @@ -38,11 +33,7 @@ export class ServerlessPlugin ServerlessPluginStartDependencies > { - private readonly config: ServerlessConfig; - - constructor(private readonly initializerContext: PluginInitializerContext) { - this.config = this.initializerContext.config.get(); - } + constructor() {} public setup( _core: CoreSetup, @@ -55,17 +46,6 @@ export class ServerlessPlugin core: CoreStart, dependencies: ServerlessPluginStartDependencies ): ServerlessPluginStart { - const { developer } = this.config; - - if (developer && developer.projectSwitcher && developer.projectSwitcher.enabled) { - const { currentType } = developer.projectSwitcher; - - core.chrome.navControls.registerRight({ - order: 5000, - mount: (target) => this.mountProjectSwitcher(target, core, currentType), - }); - } - core.chrome.setChromeStyle('project'); // Casting the "chrome.projects" service to an "internal" type: this is intentional to obscure the property from Typescript. @@ -136,21 +116,4 @@ export class ServerlessPlugin } public stop() {} - - private mountProjectSwitcher( - targetDomElement: HTMLElement, - coreStart: CoreStart, - currentProjectType: ProjectType - ) { - ReactDOM.render( - - - - - , - targetDomElement - ); - - return () => ReactDOM.unmountComponentAtNode(targetDomElement); - } } diff --git a/x-pack/platform/plugins/shared/serverless/server/config.ts b/x-pack/platform/plugins/shared/serverless/server/config.ts index 96c4816bd40f0..fe042c9cbf29a 100644 --- a/x-pack/platform/plugins/shared/serverless/server/config.ts +++ b/x-pack/platform/plugins/shared/serverless/server/config.ts @@ -17,7 +17,7 @@ const configSchema = schema.object({ // Config namespace for developer-specific settings. developer: schema.maybe( schema.object({ - // Settings for the project switcher. + // Settings for the project switcher. This is now deprecated. projectSwitcher: schema.maybe( schema.object({ // Should the switcher be enabled? @@ -53,6 +53,12 @@ export const config: PluginConfigDescriptor = { exposeToBrowser: { developer: true, }, + deprecations: ({ unused }) => [ + unused('developer', { level: 'critical' }), + unused('developer.projectSwitcher', { level: 'critical' }), + unused('developer.projectSwitcher.enabled', { level: 'critical' }), + unused('developer.projectSwitcher.currentType', { level: 'critical' }), + ], }; export type ServerlessConfig = TypeOf; diff --git a/x-pack/platform/plugins/shared/serverless/server/index.ts b/x-pack/platform/plugins/shared/serverless/server/index.ts index b873dda868231..ed4222519f55c 100644 --- a/x-pack/platform/plugins/shared/serverless/server/index.ts +++ b/x-pack/platform/plugins/shared/serverless/server/index.ts @@ -5,12 +5,11 @@ * 2.0. */ -import { PluginInitializerContext } from '@kbn/core/server'; export { config } from './config'; -export const plugin = async (initializerContext: PluginInitializerContext) => { +export const plugin = async () => { const { ServerlessPlugin } = await import('./plugin'); - return new ServerlessPlugin(initializerContext); + return new ServerlessPlugin(); }; export type { diff --git a/x-pack/platform/plugins/shared/serverless/server/plugin.test.ts b/x-pack/platform/plugins/shared/serverless/server/plugin.test.ts index d002325368be6..34c456a048e78 100644 --- a/x-pack/platform/plugins/shared/serverless/server/plugin.test.ts +++ b/x-pack/platform/plugins/shared/serverless/server/plugin.test.ts @@ -6,7 +6,6 @@ */ import { coreMock } from '@kbn/core/server/mocks'; -import { config } from './config'; import { ServerlessPlugin } from './plugin'; describe('Serverless Plugin', () => { @@ -14,13 +13,7 @@ describe('Serverless Plugin', () => { let mockCoreSetup: ReturnType; let mockCoreStart: ReturnType; beforeEach(() => { - plugin = new ServerlessPlugin( - coreMock.createPluginInitializerContext( - config.schema.validate({ - enabled: true, - }) - ) - ); + plugin = new ServerlessPlugin(); mockCoreSetup = coreMock.createSetup({ pluginStartContract: {}, diff --git a/x-pack/platform/plugins/shared/serverless/server/plugin.ts b/x-pack/platform/plugins/shared/serverless/server/plugin.ts index 31a815a4327ed..782a21fb124c3 100644 --- a/x-pack/platform/plugins/shared/serverless/server/plugin.ts +++ b/x-pack/platform/plugins/shared/serverless/server/plugin.ts @@ -5,13 +5,7 @@ * 2.0. */ -import { writeFileSync } from 'fs'; -import { resolve } from 'path'; - -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; -import { schema, TypeOf } from '@kbn/config-schema'; -import { getConfigDirectory } from '@kbn/utils'; -import { ProjectType } from '@kbn/serverless-types'; +import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; import { ALL_COMMON_SETTINGS } from '@kbn/serverless-common-settings'; import type { @@ -20,24 +14,6 @@ import type { ServerlessServerSetupDependencies, ServerlessServerStartDependencies, } from './types'; -import { ServerlessConfig } from './config'; -import { API_SWITCH_PROJECT } from '../common'; - -const switchBodySchema = schema.object({ - id: schema.oneOf([ - schema.literal('observability'), - schema.literal('security'), - schema.literal('search'), - ]), -}); - -type SwitchReqBody = TypeOf; - -const typeToIdMap: Record = { - observability: 'oblt', - security: 'security', - search: 'es', -}; export class ServerlessPlugin implements @@ -48,7 +24,6 @@ export class ServerlessPlugin ServerlessServerStartDependencies > { - private readonly config: ServerlessConfig; private projectSettingsAdded: boolean = false; private setupProjectSettings(core: CoreSetup, keys: string[]): void { @@ -57,52 +32,9 @@ export class ServerlessPlugin this.projectSettingsAdded = true; } - constructor(private readonly context: PluginInitializerContext) { - this.config = this.context.config.get(); - } + constructor() {} public setup(core: CoreSetup) { - const router = core.http.createRouter(); - const { developer } = this.config; - - // If we're in development mode, and the switcher is enabled, register the - // API endpoint responsible for switching projects. - if (process.env.NODE_ENV !== 'production' && developer?.projectSwitcher?.enabled) { - router.post( - { - path: API_SWITCH_PROJECT, - security: { - authz: { - enabled: false, - reason: - 'This route is opted out from authorization, because it is only used in development', - }, - }, - validate: { - body: switchBodySchema, - }, - }, - async (_context, request, response) => { - const { id } = request.body; - const selectedProjectType = typeToIdMap[id]; - - try { - // The switcher is not enabled by default, in cases where one has started Serverless - // with a specific config. So in this case, to ensure the switcher remains enabled, - // write the selected config to `recent` and tack on the setting to enable the switcher. - writeFileSync( - resolve(getConfigDirectory(), 'serverless.recent.dev.yml'), - `xpack.serverless.plugin.developer.projectSwitcher.enabled: true\nserverless: ${selectedProjectType}\n` - ); - - return response.ok({ body: id }); - } catch (e) { - return response.badRequest({ body: e }); - } - } - ); - } - return { setupProjectSettings: (keys: string[]) => this.setupProjectSettings(core, keys), }; diff --git a/x-pack/platform/plugins/shared/serverless/tsconfig.json b/x-pack/platform/plugins/shared/serverless/tsconfig.json index 988e3f7a693aa..c43975de8a54f 100644 --- a/x-pack/platform/plugins/shared/serverless/tsconfig.json +++ b/x-pack/platform/plugins/shared/serverless/tsconfig.json @@ -16,9 +16,6 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/core", - "@kbn/serverless-project-switcher", - "@kbn/serverless-types", - "@kbn/utils", "@kbn/core-chrome-browser", "@kbn/core-chrome-browser-internal", "@kbn/cloud-plugin", diff --git a/x-pack/platform/plugins/shared/task_manager/server/integration_tests/managed_configuration.test.ts b/x-pack/platform/plugins/shared/task_manager/server/integration_tests/managed_configuration.test.ts index 2af8c72190c5a..368770401b4f3 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/integration_tests/managed_configuration.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/integration_tests/managed_configuration.test.ts @@ -9,12 +9,33 @@ import sinon from 'sinon'; import { Client } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; import { SavedObjectsErrorHelpers, Logger } from '@kbn/core/server'; +import { schema } from '@kbn/config-schema'; import { ADJUST_THROUGHPUT_INTERVAL } from '../lib/create_managed_configuration'; import { TaskManagerPlugin, TaskManagerStartContract } from '../plugin'; import { coreMock } from '@kbn/core/server/mocks'; import { TaskManagerConfig } from '../config'; import { BulkUpdateError } from '../lib/bulk_update_error'; +const mockTaskTypeRunFn = jest.fn(); +const mockCreateTaskRunner = jest.fn(); +const mockTaskType = { + title: '', + description: '', + stateSchemaByVersion: { + 1: { + up: (state: Record) => ({ ...state, baz: state.baz || '' }), + schema: schema.object({ + foo: schema.string(), + bar: schema.string(), + baz: schema.string(), + }), + }, + }, + createTaskRunner: mockCreateTaskRunner.mockImplementation(() => ({ + run: mockTaskTypeRunFn, + })), +}; + describe('managed configuration', () => { let taskManagerStart: TaskManagerStartContract; let logger: Logger; @@ -36,56 +57,58 @@ describe('managed configuration', () => { }, }; + const config = { + discovery: { + active_nodes_lookback: '30s', + interval: 10000, + }, + kibanas_per_partition: 2, + capacity: 10, + max_attempts: 9, + poll_interval: 3000, + allow_reading_invalid_state: false, + version_conflict_threshold: 80, + monitored_aggregated_stats_refresh_rate: 60000, + monitored_stats_health_verbose_log: { + enabled: false, + level: 'debug' as const, + warn_delayed_task_start_in_seconds: 60, + }, + monitored_stats_required_freshness: 4000, + monitored_stats_running_average_window: 50, + request_capacity: 1000, + monitored_task_execution_thresholds: { + default: { + error_threshold: 90, + warn_threshold: 80, + }, + custom: {}, + }, + unsafe: { + exclude_task_types: [], + authenticate_background_task_utilization: true, + }, + event_loop_delay: { + monitor: true, + warn_threshold: 5000, + }, + worker_utilization_running_average_window: 5, + metrics_reset_interval: 3000, + claim_strategy: 'update_by_query', + request_timeouts: { + update_by_query: 1000, + }, + auto_calculate_default_ech_capacity: false, + }; + afterEach(() => clock.restore()); - describe('managed poll interval', () => { + describe('managed poll interval with default claim strategy', () => { beforeEach(async () => { jest.resetAllMocks(); clock = sinon.useFakeTimers(); - const context = coreMock.createPluginInitializerContext({ - discovery: { - active_nodes_lookback: '30s', - interval: 10000, - }, - kibanas_per_partition: 2, - capacity: 10, - max_attempts: 9, - poll_interval: 3000, - allow_reading_invalid_state: false, - version_conflict_threshold: 80, - monitored_aggregated_stats_refresh_rate: 60000, - monitored_stats_health_verbose_log: { - enabled: false, - level: 'debug' as const, - warn_delayed_task_start_in_seconds: 60, - }, - monitored_stats_required_freshness: 4000, - monitored_stats_running_average_window: 50, - request_capacity: 1000, - monitored_task_execution_thresholds: { - default: { - error_threshold: 90, - warn_threshold: 80, - }, - custom: {}, - }, - unsafe: { - exclude_task_types: [], - authenticate_background_task_utilization: true, - }, - event_loop_delay: { - monitor: true, - warn_threshold: 5000, - }, - worker_utilization_running_average_window: 5, - metrics_reset_interval: 3000, - claim_strategy: 'update_by_query', - request_timeouts: { - update_by_query: 1000, - }, - auto_calculate_default_ech_capacity: false, - }); + const context = coreMock.createPluginInitializerContext(config); logger = context.logger.get('taskManager'); const taskManager = new TaskManagerPlugin(context); @@ -127,10 +150,7 @@ describe('managed configuration', () => { clock.tick(ADJUST_THROUGHPUT_INTERVAL); expect(logger.warn).toHaveBeenCalledWith( - 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' + 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' ); expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); }); @@ -154,10 +174,7 @@ describe('managed configuration', () => { clock.tick(100000); expect(logger.warn).toHaveBeenCalledWith( - 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Poll interval configuration changing from 3000 to 61000 after seeing 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' + 'Poll interval configuration changing from 3000 to 61000 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' ); expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 61000ms'); }); @@ -175,63 +192,119 @@ describe('managed configuration', () => { clock.tick(ADJUST_THROUGHPUT_INTERVAL); expect(logger.warn).toHaveBeenCalledWith( - 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' - ); - expect(logger.debug).toHaveBeenCalledWith( - 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' + 'Poll interval configuration changing from 3000 to 3600 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' ); expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); }); }); - describe('managed capacity with default claim strategy', () => { + describe('managed poll interval with mget claim strategy', () => { beforeEach(async () => { jest.resetAllMocks(); clock = sinon.useFakeTimers(); const context = coreMock.createPluginInitializerContext({ - discovery: { - active_nodes_lookback: '30s', - interval: 10000, - }, - kibanas_per_partition: 2, - capacity: 10, - max_attempts: 9, - poll_interval: 3000, - allow_reading_invalid_state: false, - version_conflict_threshold: 80, - monitored_aggregated_stats_refresh_rate: 60000, - monitored_stats_health_verbose_log: { - enabled: false, - level: 'debug' as const, - warn_delayed_task_start_in_seconds: 60, - }, - monitored_stats_required_freshness: 4000, - monitored_stats_running_average_window: 50, - request_capacity: 1000, - monitored_task_execution_thresholds: { - default: { - error_threshold: 90, - warn_threshold: 80, - }, - custom: {}, - }, - unsafe: { - exclude_task_types: [], - authenticate_background_task_utilization: true, - }, - event_loop_delay: { - monitor: true, - warn_threshold: 5000, - }, - worker_utilization_running_average_window: 5, - metrics_reset_interval: 3000, - claim_strategy: 'update_by_query', - request_timeouts: { - update_by_query: 1000, - }, - auto_calculate_default_ech_capacity: false, + ...config, + poll_interval: 500, + claim_strategy: 'mget', + }); + logger = context.logger.get('taskManager'); + + const taskManager = new TaskManagerPlugin(context); + ( + await taskManager.setup(coreMock.createSetup(), { usageCollection: undefined }) + ).registerTaskDefinitions({ + fooType: mockTaskType, + }); + + const coreStart = coreMock.createStart(); + coreStart.elasticsearch = esStart; + esStart.client.asInternalUser.child.mockReturnValue( + esStart.client.asInternalUser as unknown as Client + ); + coreStart.savedObjects.createInternalRepository.mockReturnValue(savedObjectsClient); + taskManagerStart = taskManager.start(coreStart, {}); + + // force rxjs timers to fire when they are scheduled for setTimeout(0) as the + // sinon fake timers cause them to stall + clock.tick(0); + }); + + test('should increase poll interval when Elasticsearch returns 429 error', async () => { + savedObjectsClient.create.mockRejectedValueOnce( + SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') + ); + + // Cause "too many requests" error to be thrown + await expect( + taskManagerStart.schedule({ + taskType: 'fooType', + params: { foo: true }, + state: { foo: 'test', bar: 'test', baz: 'test' }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration changing from 500 to 600 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 600ms'); + }); + + test('should increase poll interval when Elasticsearch returns "cannot execute [inline] scripts" error', async () => { + const childEsClient = esStart.client.asInternalUser.child({}) as jest.Mocked; + childEsClient.search.mockImplementationOnce(async () => { + throw inlineScriptError; }); + + await expect(taskManagerStart.fetch({})).rejects.toThrowErrorMatchingInlineSnapshot( + `"cannot execute [inline] scripts\\" error"` + ); + + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration changing from 500 to 600 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 600ms'); + }); + + test('should increase poll interval TM untilization is low', async () => { + savedObjectsClient.create.mockImplementationOnce( + async (type: string, attributes: unknown) => ({ + id: 'testid', + type, + attributes, + references: [], + version: '123', + }) + ); + + await taskManagerStart.schedule({ + taskType: 'fooType', + params: { foo: true }, + state: { foo: 'test', bar: 'test', baz: 'test' }, + }); + + mockTaskTypeRunFn.mockImplementation(() => { + return { state: {} }; + }); + + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + + expect(logger.debug).toHaveBeenCalledWith( + 'Poll interval configuration changing from 500 to 3000 after a change in the average task load: 0.' + ); + expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3000ms'); + }); + }); + + describe('managed capacity with default claim strategy', () => { + beforeEach(async () => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + + const context = coreMock.createPluginInitializerContext(config); logger = context.logger.get('taskManager'); const taskManager = new TaskManagerPlugin(context); @@ -312,47 +385,8 @@ describe('managed configuration', () => { clock = sinon.useFakeTimers(); const context = coreMock.createPluginInitializerContext({ - discovery: { - active_nodes_lookback: '30s', - interval: 10000, - }, - kibanas_per_partition: 2, - capacity: 10, - max_attempts: 9, - poll_interval: 3000, - allow_reading_invalid_state: false, - version_conflict_threshold: 80, - monitored_aggregated_stats_refresh_rate: 60000, - monitored_stats_health_verbose_log: { - enabled: false, - level: 'debug' as const, - warn_delayed_task_start_in_seconds: 60, - }, - monitored_stats_required_freshness: 4000, - monitored_stats_running_average_window: 50, - request_capacity: 1000, - monitored_task_execution_thresholds: { - default: { - error_threshold: 90, - warn_threshold: 80, - }, - custom: {}, - }, - unsafe: { - exclude_task_types: [], - authenticate_background_task_utilization: true, - }, - event_loop_delay: { - monitor: true, - warn_threshold: 5000, - }, - worker_utilization_running_average_window: 5, - metrics_reset_interval: 3000, + ...config, claim_strategy: 'mget', - request_timeouts: { - update_by_query: 1000, - }, - auto_calculate_default_ech_capacity: false, }); logger = context.logger.get('taskManager'); diff --git a/x-pack/platform/plugins/shared/task_manager/server/integration_tests/removed_types.test.ts b/x-pack/platform/plugins/shared/task_manager/server/integration_tests/removed_types.test.ts index 96d25eb80feea..1ae0e19e31018 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/integration_tests/removed_types.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/integration_tests/removed_types.test.ts @@ -28,7 +28,8 @@ jest.mock('../monitoring/workload_statistics', () => { }; }); -describe('unrecognized task types', () => { +// FLAKY: https://github.com/elastic/kibana/issues/208459 +describe.skip('unrecognized task types', () => { let esServer: TestElasticsearchUtils; let kibanaServer: TestKibanaUtils; let taskManagerPlugin: TaskManagerStartContract; diff --git a/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.test.ts b/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.test.ts index cd13ac20026ed..1ce5680b465de 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.test.ts @@ -6,17 +6,27 @@ */ import sinon from 'sinon'; -import { Subject } from 'rxjs'; +import { Subject, startWith, distinctUntilChanged, BehaviorSubject, withLatestFrom } from 'rxjs'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { - createManagedConfiguration, ADJUST_THROUGHPUT_INTERVAL, + calculateStartingCapacity, + countErrors, + createCapacityScan, + createPollIntervalScan, INTERVAL_AFTER_BLOCK_EXCEPTION, } from './create_managed_configuration'; import { mockLogger } from '../test_utils'; -import { CLAIM_STRATEGY_UPDATE_BY_QUERY, CLAIM_STRATEGY_MGET, TaskManagerConfig } from '../config'; +import { + CLAIM_STRATEGY_UPDATE_BY_QUERY, + CLAIM_STRATEGY_MGET, + TaskManagerConfig, + DEFAULT_CAPACITY, + DEFAULT_POLL_INTERVAL, +} from '../config'; import { MsearchError } from './msearch_error'; import { BulkUpdateError } from './bulk_update_error'; +import { createRunningAveragedStat } from '../monitoring/task_run_calculators'; describe('createManagedConfiguration()', () => { let clock: sinon.SinonFakeTimers; @@ -29,123 +39,66 @@ describe('createManagedConfiguration()', () => { afterEach(() => clock.restore()); - test('returns observables with initialized values', async () => { - const capacitySubscription = jest.fn(); - const pollIntervalSubscription = jest.fn(); - const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ - logger, - errors$: new Subject(), - config: { - capacity: 20, - poll_interval: 2, - } as TaskManagerConfig, - }); - capacityConfiguration$.subscribe(capacitySubscription); - pollIntervalConfiguration$.subscribe(pollIntervalSubscription); - expect(capacitySubscription).toHaveBeenCalledTimes(1); - expect(capacitySubscription).toHaveBeenNthCalledWith(1, 20); - expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); - expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); - }); - test('uses max_workers config as capacity if only max workers is defined', async () => { - const capacitySubscription = jest.fn(); - const pollIntervalSubscription = jest.fn(); - const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ - logger, - errors$: new Subject(), - config: { + const capacity = calculateStartingCapacity( + { max_workers: 10, poll_interval: 2, } as TaskManagerConfig, - }); - capacityConfiguration$.subscribe(capacitySubscription); - pollIntervalConfiguration$.subscribe(pollIntervalSubscription); - expect(capacitySubscription).toHaveBeenCalledTimes(1); - expect(capacitySubscription).toHaveBeenNthCalledWith(1, 10); - expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); - expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + logger, + DEFAULT_CAPACITY + ); + expect(capacity).toBe(10); }); test('uses max_workers config as capacity but does not exceed MAX_CAPACITY', async () => { - const capacitySubscription = jest.fn(); - const pollIntervalSubscription = jest.fn(); - const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ - logger, - errors$: new Subject(), - config: { + const capacity = calculateStartingCapacity( + { max_workers: 1000, poll_interval: 2, } as TaskManagerConfig, - }); - capacityConfiguration$.subscribe(capacitySubscription); - pollIntervalConfiguration$.subscribe(pollIntervalSubscription); - expect(capacitySubscription).toHaveBeenCalledTimes(1); - expect(capacitySubscription).toHaveBeenNthCalledWith(1, 50); - expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); - expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + logger, + DEFAULT_CAPACITY + ); + expect(capacity).toBe(50); }); test('uses provided defaultCapacity if neither capacity nor max_workers is defined', async () => { - const capacitySubscription = jest.fn(); - const pollIntervalSubscription = jest.fn(); - const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ - defaultCapacity: 500, - logger, - errors$: new Subject(), - config: { + const capacity = calculateStartingCapacity( + { poll_interval: 2, } as TaskManagerConfig, - }); - capacityConfiguration$.subscribe(capacitySubscription); - pollIntervalConfiguration$.subscribe(pollIntervalSubscription); - expect(capacitySubscription).toHaveBeenCalledTimes(1); - expect(capacitySubscription).toHaveBeenNthCalledWith(1, 500); - expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); - expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + logger, + 500 + ); + expect(capacity).toBe(500); }); test('logs warning and uses capacity config if both capacity and max_workers is defined', async () => { - const capacitySubscription = jest.fn(); - const pollIntervalSubscription = jest.fn(); - const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ - logger, - errors$: new Subject(), - config: { + const capacity = calculateStartingCapacity( + { capacity: 30, max_workers: 10, poll_interval: 2, } as TaskManagerConfig, - }); - capacityConfiguration$.subscribe(capacitySubscription); - pollIntervalConfiguration$.subscribe(pollIntervalSubscription); - expect(capacitySubscription).toHaveBeenCalledTimes(1); - expect(capacitySubscription).toHaveBeenNthCalledWith(1, 30); - expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); - expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + logger, + 500 + ); + expect(capacity).toBe(30); expect(logger.warn).toHaveBeenCalledWith( `Both \"xpack.task_manager.capacity\" and \"xpack.task_manager.max_workers\" configs are set, max_workers will be ignored in favor of capacity and the setting should be removed.` ); }); test(`skips errors that aren't about too many requests`, async () => { - const capacitySubscription = jest.fn(); - const pollIntervalSubscription = jest.fn(); + const errorSubscription = jest.fn(); const errors$ = new Subject(); - const { capacityConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ - errors$, - logger, - config: { - capacity: 10, - poll_interval: 100, - } as TaskManagerConfig, - }); - capacityConfiguration$.subscribe(capacitySubscription); - pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + const errorCheck$ = countErrors(errors$, ADJUST_THROUGHPUT_INTERVAL); + errorCheck$.subscribe(errorSubscription); + errors$.next(new Error('foo')); clock.tick(ADJUST_THROUGHPUT_INTERVAL); - expect(capacitySubscription).toHaveBeenCalledTimes(1); - expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(errorSubscription).toHaveBeenCalledTimes(1); }); describe('capacity configuration', () => { @@ -154,16 +107,21 @@ describe('createManagedConfiguration()', () => { claimStrategy: string = CLAIM_STRATEGY_UPDATE_BY_QUERY ) { const errors$ = new Subject(); + const errorCheck$ = countErrors(errors$, ADJUST_THROUGHPUT_INTERVAL); const subscription = jest.fn(); - const { capacityConfiguration$ } = createManagedConfiguration({ - errors$, - logger, - config: { - capacity: startingCapacity, - poll_interval: 1, - claim_strategy: claimStrategy, - } as TaskManagerConfig, - }); + const capacityConfiguration$ = errorCheck$.pipe( + createCapacityScan( + { + capacity: startingCapacity, + poll_interval: 1, + claim_strategy: claimStrategy, + } as TaskManagerConfig, + logger, + startingCapacity + ), + startWith(startingCapacity), + distinctUntilChanged() + ); capacityConfiguration$.subscribe(subscription); return { subscription, errors$ }; } @@ -369,19 +327,23 @@ describe('createManagedConfiguration()', () => { }); describe('pollInterval configuration', () => { - function setupScenario(startingPollInterval: number) { + function setupScenario( + startingPollInterval: number, + claimStrategy: string = CLAIM_STRATEGY_UPDATE_BY_QUERY + ) { const errors$ = new Subject(); + const utilization$ = new BehaviorSubject(100); + const errorCheck$ = countErrors(errors$, ADJUST_THROUGHPUT_INTERVAL); const subscription = jest.fn(); - const { pollIntervalConfiguration$ } = createManagedConfiguration({ - logger, - errors$, - config: { - poll_interval: startingPollInterval, - capacity: 20, - } as TaskManagerConfig, - }); + const queue = createRunningAveragedStat(5); + const pollIntervalConfiguration$ = errorCheck$.pipe( + withLatestFrom(utilization$), + createPollIntervalScan(logger, startingPollInterval, claimStrategy, queue), + startWith(startingPollInterval), + distinctUntilChanged() + ); pollIntervalConfiguration$.subscribe(subscription); - return { subscription, errors$ }; + return { subscription, errors$, utilization$ }; } beforeEach(() => { @@ -391,139 +353,226 @@ describe('createManagedConfiguration()', () => { afterEach(() => clock.restore()); - test('should increase configuration at the next interval when an error is emitted', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); - expect(subscription).toHaveBeenCalledTimes(1); - clock.tick(1); - expect(subscription).toHaveBeenCalledTimes(2); - expect(subscription).toHaveBeenNthCalledWith(2, 120); - }); + describe('default claim strategy', () => { + test('should increase configuration at the next interval when an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + }); - test('should increase configuration at the next interval when a 500 error is emitted', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.decorateGeneralError(new Error('a'), 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); - expect(subscription).toHaveBeenCalledTimes(1); - clock.tick(1); - expect(subscription).toHaveBeenCalledTimes(2); - expect(subscription).toHaveBeenNthCalledWith(2, 120); - }); + test('should increase configuration at the next interval when a 500 error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.decorateGeneralError(new Error('a'), 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + }); - test('should increase configuration at the next interval when a 503 error is emitted', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); - expect(subscription).toHaveBeenCalledTimes(1); - clock.tick(1); - expect(subscription).toHaveBeenCalledTimes(2); - expect(subscription).toHaveBeenNthCalledWith(2, 120); - }); + test('should increase configuration at the next interval when a 503 error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + }); - test('should increase configuration at the next interval when an error with cluster_block_exception type is emitted, then decreases back to normal', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next( - new BulkUpdateError({ - statusCode: 403, - message: 'index is blocked', - type: 'cluster_block_exception', - }) - ); - expect(subscription).toHaveBeenNthCalledWith(1, 100); - // It emits the error with cluster_block_exception type immediately - expect(subscription).toHaveBeenNthCalledWith(2, INTERVAL_AFTER_BLOCK_EXCEPTION); - clock.tick(INTERVAL_AFTER_BLOCK_EXCEPTION); - expect(subscription).toHaveBeenCalledTimes(3); - expect(subscription).toHaveBeenNthCalledWith(3, 100); - }); + test('should increase configuration at the next interval when an error with cluster_block_exception type is emitted, then decreases back to normal', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next( + new BulkUpdateError({ + statusCode: 403, + message: 'index is blocked', + type: 'cluster_block_exception', + }) + ); + expect(subscription).toHaveBeenNthCalledWith(1, 100); + // It emits the error with cluster_block_exception type immediately + expect(subscription).toHaveBeenNthCalledWith(2, INTERVAL_AFTER_BLOCK_EXCEPTION); + clock.tick(INTERVAL_AFTER_BLOCK_EXCEPTION); + expect(subscription).toHaveBeenCalledTimes(3); + expect(subscription).toHaveBeenNthCalledWith(3, 100); + }); - test('should log a warning when the configuration changes from the starting value', async () => { - const { errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - expect(logger.warn).toHaveBeenCalledWith( - 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).' - ); - }); + test('should log a warning when the configuration changes from the starting value', async () => { + const { errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration changing from 100 to 120 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' + ); + }); - test('should log a warning when an issue occurred in the calculating of the increased poll interval', async () => { - const { errors$ } = setupScenario(NaN); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - expect(logger.error).toHaveBeenCalledWith( - 'Poll interval configuration had an issue calculating the new poll interval: Math.min(Math.ceil(NaN * 1.2), Math.max(60000, NaN)) = NaN, will keep the poll interval unchanged (NaN)' - ); - }); + test('should log a warning when an issue occurred in the calculating of the increased poll interval', async () => { + const { errors$ } = setupScenario(NaN); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.error).toHaveBeenCalledWith( + 'Poll interval configuration had an issue calculating the new poll interval: Math.min(Math.ceil(NaN * 1.2), Math.max(60000, NaN)) = NaN, will keep the poll interval unchanged (NaN)' + ); + }); - test('should log a warning when an issue occurred in the calculating of the decreased poll interval', async () => { - setupScenario(NaN); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - expect(logger.error).toHaveBeenCalledWith( - 'Poll interval configuration had an issue calculating the new poll interval: Math.max(NaN, Math.floor(NaN * 0.95)) = NaN, will keep the poll interval unchanged (NaN)' - ); - }); + test('should log a warning when an issue occurred in the calculating of the decreased poll interval', async () => { + setupScenario(NaN); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.error).toHaveBeenCalledWith( + 'Poll interval configuration had an issue calculating the new poll interval: Math.max(NaN, Math.floor(NaN * 0.95)) = NaN, will keep the poll interval unchanged (NaN)' + ); + }); + + test('should decrease configuration back to normal incrementally after an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + expect(subscription).toHaveBeenNthCalledWith(3, 114); + // 108.3 -> 108 from Math.floor + expect(subscription).toHaveBeenNthCalledWith(4, 108); + expect(subscription).toHaveBeenNthCalledWith(5, 102); + // 96.9 -> 100 from Math.max with the starting value + expect(subscription).toHaveBeenNthCalledWith(6, 100); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(6); + }); - test('should decrease configuration back to normal incrementally after an error is emitted', async () => { - const { subscription, errors$ } = setupScenario(100); - errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); - expect(subscription).toHaveBeenNthCalledWith(2, 120); - expect(subscription).toHaveBeenNthCalledWith(3, 114); - // 108.3 -> 108 from Math.floor - expect(subscription).toHaveBeenNthCalledWith(4, 108); - expect(subscription).toHaveBeenNthCalledWith(5, 102); - // 96.9 -> 100 from Math.max with the starting value - expect(subscription).toHaveBeenNthCalledWith(6, 100); - // No new calls due to value not changing and usage of distinctUntilChanged() - expect(subscription).toHaveBeenCalledTimes(6); + test('should increase configuration when errors keep emitting', async () => { + const { subscription, errors$ } = setupScenario(100); + for (let i = 0; i < 3; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(2, 120); + expect(subscription).toHaveBeenNthCalledWith(3, 144); + // 172.8 -> 173 from Math.ceil + expect(subscription).toHaveBeenNthCalledWith(4, 173); + }); + + test('should limit the upper bound to 60s by default', async () => { + const { subscription, errors$ } = setupScenario(3000); + for (let i = 0; i < 18; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(2, 3600); + expect(subscription).toHaveBeenNthCalledWith(3, 4320); + expect(subscription).toHaveBeenNthCalledWith(4, 5184); + expect(subscription).toHaveBeenNthCalledWith(5, 6221); + expect(subscription).toHaveBeenNthCalledWith(6, 7466); + expect(subscription).toHaveBeenNthCalledWith(7, 8960); + expect(subscription).toHaveBeenNthCalledWith(8, 10752); + expect(subscription).toHaveBeenNthCalledWith(9, 12903); + expect(subscription).toHaveBeenNthCalledWith(10, 15484); + expect(subscription).toHaveBeenNthCalledWith(11, 18581); + expect(subscription).toHaveBeenNthCalledWith(12, 22298); + expect(subscription).toHaveBeenNthCalledWith(13, 26758); + expect(subscription).toHaveBeenNthCalledWith(14, 32110); + expect(subscription).toHaveBeenNthCalledWith(15, 38532); + expect(subscription).toHaveBeenNthCalledWith(16, 46239); + expect(subscription).toHaveBeenNthCalledWith(17, 55487); + expect(subscription).toHaveBeenNthCalledWith(18, 60000); + }); + + test('should not adjust poll interval dynamically if initial value is > 60s', async () => { + const { subscription, errors$ } = setupScenario(65000); + for (let i = 0; i < 5; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenCalledTimes(1); + expect(subscription).toHaveBeenNthCalledWith(1, 65000); + }); }); - test('should increase configuration when errors keep emitting', async () => { - const { subscription, errors$ } = setupScenario(100); - for (let i = 0; i < 3; i++) { + describe('mget claim strategy', () => { + test('should increase configuration at the next interval when an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100, CLAIM_STRATEGY_MGET); errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); - clock.tick(ADJUST_THROUGHPUT_INTERVAL); - } - expect(subscription).toHaveBeenNthCalledWith(2, 120); - expect(subscription).toHaveBeenNthCalledWith(3, 144); - // 172.8 -> 173 from Math.ceil - expect(subscription).toHaveBeenNthCalledWith(4, 173); - }); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + }); - test('should limit the upper bound to 60s by default', async () => { - const { subscription, errors$ } = setupScenario(3000); - for (let i = 0; i < 18; i++) { + test('should log a warning when the configuration changes from the starting value', async () => { + const { errors$ } = setupScenario(100, CLAIM_STRATEGY_MGET); errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); clock.tick(ADJUST_THROUGHPUT_INTERVAL); - } - expect(subscription).toHaveBeenNthCalledWith(2, 3600); - expect(subscription).toHaveBeenNthCalledWith(3, 4320); - expect(subscription).toHaveBeenNthCalledWith(4, 5184); - expect(subscription).toHaveBeenNthCalledWith(5, 6221); - expect(subscription).toHaveBeenNthCalledWith(6, 7466); - expect(subscription).toHaveBeenNthCalledWith(7, 8960); - expect(subscription).toHaveBeenNthCalledWith(8, 10752); - expect(subscription).toHaveBeenNthCalledWith(9, 12903); - expect(subscription).toHaveBeenNthCalledWith(10, 15484); - expect(subscription).toHaveBeenNthCalledWith(11, 18581); - expect(subscription).toHaveBeenNthCalledWith(12, 22298); - expect(subscription).toHaveBeenNthCalledWith(13, 26758); - expect(subscription).toHaveBeenNthCalledWith(14, 32110); - expect(subscription).toHaveBeenNthCalledWith(15, 38532); - expect(subscription).toHaveBeenNthCalledWith(16, 46239); - expect(subscription).toHaveBeenNthCalledWith(17, 55487); - expect(subscription).toHaveBeenNthCalledWith(18, 60000); - }); + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration changing from 100 to 120 after seeing 1 "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).' + ); + }); + + test('should decrease configuration back to normal incrementally after an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(DEFAULT_POLL_INTERVAL, CLAIM_STRATEGY_MGET); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(2, 3600); + expect(subscription).toHaveBeenNthCalledWith(3, 3420); + expect(subscription).toHaveBeenNthCalledWith(4, 3249); + expect(subscription).toHaveBeenNthCalledWith(5, 3086); + expect(subscription).toHaveBeenNthCalledWith(6, 3000); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(6); + }); - test('should not adjust poll interval dynamically if initial value is > 60s', async () => { - const { subscription, errors$ } = setupScenario(65000); - for (let i = 0; i < 5; i++) { + test('should decrease configuration after error and reset to initial poll interval when poll interval < default and TM utilization > 25%', async () => { + const { subscription, errors$ } = setupScenario(2800, CLAIM_STRATEGY_MGET); errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(2, 3360); + expect(subscription).toHaveBeenNthCalledWith(3, 3192); + expect(subscription).toHaveBeenNthCalledWith(4, 3032); + expect(subscription).toHaveBeenNthCalledWith(5, 2800); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(5); + }); + + test('should decrease configuration after error and reset to default poll interval when poll interval < default and TM utilization < 25%', async () => { + const { subscription, errors$, utilization$ } = setupScenario(2800, CLAIM_STRATEGY_MGET); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + for (let i = 0; i < 10; i++) { + utilization$.next(20); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(2, 3360); + expect(subscription).toHaveBeenNthCalledWith(3, 3192); + expect(subscription).toHaveBeenNthCalledWith(4, 3032); + expect(subscription).toHaveBeenNthCalledWith(5, 3000); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(5); + }); + + test('should change configuration based on TM utilization', async () => { + const { subscription, utilization$ } = setupScenario(500, CLAIM_STRATEGY_MGET); + const u = [15, 35, 5, 48, 0]; + for (let i = 0; i < u.length; i++) { + utilization$.next(u[i]); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(2, 3000); + expect(subscription).toHaveBeenNthCalledWith(3, 500); + expect(subscription).toHaveBeenNthCalledWith(4, 3000); + expect(subscription).toHaveBeenNthCalledWith(5, 500); + expect(subscription).toHaveBeenNthCalledWith(6, 3000); + expect(subscription).toHaveBeenCalledTimes(6); + }); + + test('should log a warning when the configuration changes from the starting value based on TM utilization', async () => { + const { utilization$ } = setupScenario(100, CLAIM_STRATEGY_MGET); + utilization$.next(20); clock.tick(ADJUST_THROUGHPUT_INTERVAL); - } - expect(subscription).toHaveBeenCalledTimes(1); - expect(subscription).toHaveBeenNthCalledWith(1, 65000); + expect(logger.debug).toHaveBeenCalledWith( + 'Poll interval configuration changing from 100 to 3000 after a change in the average task load: 20.' + ); + }); }); }); }); diff --git a/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.ts b/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.ts index 2105f29e9c617..0aaef162e6849 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/lib/create_managed_configuration.ts @@ -5,12 +5,18 @@ * 2.0. */ +import stats from 'stats-lite'; import { interval, merge, of, Observable } from 'rxjs'; -import { filter, mergeScan, map, scan, distinctUntilChanged, startWith } from 'rxjs'; +import { filter, mergeScan, map, scan } from 'rxjs'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { Logger } from '@kbn/core/server'; import { isEsCannotExecuteScriptError } from './identify_es_error'; -import { CLAIM_STRATEGY_MGET, DEFAULT_CAPACITY, MAX_CAPACITY, TaskManagerConfig } from '../config'; +import { + CLAIM_STRATEGY_MGET, + DEFAULT_POLL_INTERVAL, + MAX_CAPACITY, + TaskManagerConfig, +} from '../config'; import { TaskCost } from '../task'; import { getMsearchStatusCode } from './msearch_error'; import { getBulkUpdateStatusCode, isClusterBlockException } from './bulk_update_error'; @@ -40,49 +46,16 @@ const CAPACITY_INCREASE_PERCENTAGE = 1.05; const POLL_INTERVAL_DECREASE_PERCENTAGE = 0.95; const POLL_INTERVAL_INCREASE_PERCENTAGE = 1.2; -interface ManagedConfigurationOpts { - config: TaskManagerConfig; - defaultCapacity?: number; - errors$: Observable; - logger: Logger; -} - interface ErrorScanResult { count: number; isBlockException: boolean; } -export interface ManagedConfiguration { - startingCapacity: number; - capacityConfiguration$: Observable; - pollIntervalConfiguration$: Observable; -} - -export function createManagedConfiguration({ - config, - defaultCapacity = DEFAULT_CAPACITY, - logger, - errors$, -}: ManagedConfigurationOpts): ManagedConfiguration { - const errorCheck$ = countErrors(errors$, ADJUST_THROUGHPUT_INTERVAL); - const startingCapacity = calculateStartingCapacity(config, logger, defaultCapacity); - const startingPollInterval = config.poll_interval; - return { - startingCapacity, - capacityConfiguration$: errorCheck$.pipe( - createCapacityScan(config, logger, startingCapacity), - startWith(startingCapacity), - distinctUntilChanged() - ), - pollIntervalConfiguration$: errorCheck$.pipe( - createPollIntervalScan(logger, startingPollInterval), - startWith(startingPollInterval), - distinctUntilChanged() - ), - }; -} - -function createCapacityScan(config: TaskManagerConfig, logger: Logger, startingCapacity: number) { +export function createCapacityScan( + config: TaskManagerConfig, + logger: Logger, + startingCapacity: number +) { return scan( (previousCapacity: number, { count: errorCount, isBlockException }: ErrorScanResult) => { let newCapacity: number; @@ -124,10 +97,17 @@ function createCapacityScan(config: TaskManagerConfig, logger: Logger, startingC ); } -function createPollIntervalScan(logger: Logger, startingPollInterval: number) { +export function createPollIntervalScan( + logger: Logger, + startingPollInterval: number, + claimStrategy: string, + tmUtilizationQueue: (value?: number | undefined) => number[] +) { return scan( - (previousPollInterval: number, { count: errorCount, isBlockException }: ErrorScanResult) => { + (previousPollInterval: number, [{ count: errorCount, isBlockException }, tmUtilization]) => { let newPollInterval: number; + let updatedForCapacity = false; + let avgTmUtilization = 0; if (isBlockException) { newPollInterval = INTERVAL_AFTER_BLOCK_EXCEPTION; } else { @@ -164,19 +144,32 @@ function createPollIntervalScan(logger: Logger, startingPollInterval: number) { ); newPollInterval = previousPollInterval; } + + // If the task claim strategy is mget, increase the poll interval if the the avg used capacity over 15s is less than 25%. + const queue = tmUtilizationQueue(tmUtilization); + avgTmUtilization = stats.mean(queue); + if (claimStrategy === CLAIM_STRATEGY_MGET && newPollInterval < DEFAULT_POLL_INTERVAL) { + updatedForCapacity = true; + if (avgTmUtilization < 25) { + newPollInterval = DEFAULT_POLL_INTERVAL; + } else { + // If the the used capacity is greater than or equal to 25% reset the polling interval. + newPollInterval = startingPollInterval; + } + } } } - if (newPollInterval !== previousPollInterval) { if (previousPollInterval !== INTERVAL_AFTER_BLOCK_EXCEPTION) { - logger.debug( - `Poll interval configuration changing from ${previousPollInterval} to ${newPollInterval} after seeing ${errorCount} "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).` - ); - } - if (previousPollInterval === startingPollInterval) { - logger.warn( - `Poll interval configuration is temporarily increased after Elasticsearch returned ${errorCount} "too many request" and/or "execute [inline] script" and/or "cluster_block_exception" error(s).` - ); + if (updatedForCapacity) { + logger.debug( + `Poll interval configuration changing from ${previousPollInterval} to ${newPollInterval} after a change in the average task load: ${avgTmUtilization}.` + ); + } else { + logger.warn( + `Poll interval configuration changing from ${previousPollInterval} to ${newPollInterval} after seeing ${errorCount} "too many request" and/or "execute [inline] script" error(s) and/or "cluster_block_exception" error(s).` + ); + } } } return newPollInterval; @@ -185,7 +178,7 @@ function createPollIntervalScan(logger: Logger, startingPollInterval: number) { ); } -function countErrors( +export function countErrors( errors$: Observable, countInterval: number ): Observable { diff --git a/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.test.ts b/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.test.ts index 9a0084fcdf9e4..d6c43a78296cf 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.test.ts @@ -9,8 +9,20 @@ import { Subject } from 'rxjs'; import { take, bufferCount } from 'rxjs'; import { createConfigurationAggregator } from './configuration_statistics'; import { TaskManagerConfig } from '../config'; +import { taskPollingLifecycleMock } from '../polling_lifecycle.mock'; describe('Configuration Statistics Aggregator', () => { + let mockTaskPollingLifecycle = taskPollingLifecycleMock.create({}); + const capacityConfiguration$ = new Subject(); + const pollIntervalConfiguration$ = new Subject(); + + beforeEach(() => { + mockTaskPollingLifecycle = taskPollingLifecycleMock.create({ + capacityConfiguration$, + pollIntervalConfiguration$, + }); + }); + test('merges the static config with the merged configs', async () => { const configuration: TaskManagerConfig = { discovery: { @@ -55,17 +67,11 @@ describe('Configuration Statistics Aggregator', () => { auto_calculate_default_ech_capacity: false, }; - const managedConfig = { - startingCapacity: 10, - capacityConfiguration$: new Subject(), - pollIntervalConfiguration$: new Subject(), - }; - return new Promise(async (resolve, reject) => { try { - createConfigurationAggregator(configuration, managedConfig) - .pipe(take(3), bufferCount(3)) - .subscribe(([initial, updatedWorkers, updatedInterval]) => { + createConfigurationAggregator(configuration, 10, mockTaskPollingLifecycle) + .pipe(take(2), bufferCount(2)) + .subscribe(([initial, updatedWorkers]) => { expect(initial.value).toEqual({ capacity: { config: 10, @@ -104,29 +110,9 @@ describe('Configuration Statistics Aggregator', () => { custom: {}, }, }); - expect(updatedInterval.value).toEqual({ - capacity: { - config: 8, - as_workers: 8, - as_cost: 16, - }, - claim_strategy: 'update_by_query', - poll_interval: 3000, - request_capacity: 1000, - monitored_aggregated_stats_refresh_rate: 5000, - monitored_stats_running_average_window: 50, - monitored_task_execution_thresholds: { - default: { - error_threshold: 90, - warn_threshold: 80, - }, - custom: {}, - }, - }); resolve(); }, reject); - managedConfig.capacityConfiguration$.next(8); - managedConfig.pollIntervalConfiguration$.next(3000); + capacityConfiguration$.next(8); } catch (error) { reject(error); } diff --git a/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.ts b/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.ts index 8d963798174e6..4f08828605e58 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/monitoring/configuration_statistics.ts @@ -11,8 +11,8 @@ import { map, startWith } from 'rxjs'; import { JsonObject } from '@kbn/utility-types'; import { AggregatedStatProvider } from '../lib/runtime_statistics_aggregator'; import { CLAIM_STRATEGY_UPDATE_BY_QUERY, TaskManagerConfig } from '../config'; -import { ManagedConfiguration } from '../lib/create_managed_configuration'; import { getCapacityInCost, getCapacityInWorkers } from '../task_pool'; +import { TaskPollingLifecycle } from '../polling_lifecycle'; const CONFIG_FIELDS_TO_EXPOSE = [ 'request_capacity', @@ -37,27 +37,33 @@ export type ConfigStat = Pick< export function createConfigurationAggregator( config: TaskManagerConfig, - managedConfig: ManagedConfiguration + startingCapacity: number, + taskPollingLifecycle?: TaskPollingLifecycle ): AggregatedStatProvider { + const capacity$ = taskPollingLifecycle + ? taskPollingLifecycle.capacityConfiguration$.pipe( + startWith(startingCapacity), + map((capacity) => ({ + capacity: { + config: capacity, + as_workers: getCapacityInWorkers(capacity), + as_cost: getCapacityInCost(capacity), + }, + })) + ) + : of({ + capacity: { + config: startingCapacity, + as_workers: getCapacityInWorkers(startingCapacity), + as_cost: getCapacityInCost(startingCapacity), + }, + }); + return combineLatest([ of(pick(config, ...CONFIG_FIELDS_TO_EXPOSE)), of({ claim_strategy: config.claim_strategy ?? CLAIM_STRATEGY_UPDATE_BY_QUERY }), - managedConfig.pollIntervalConfiguration$.pipe( - startWith(config.poll_interval), - map>((pollInterval) => ({ - poll_interval: pollInterval, - })) - ), - managedConfig.capacityConfiguration$.pipe( - startWith(managedConfig.startingCapacity), - map((capacity) => ({ - capacity: { - config: capacity, - as_workers: getCapacityInWorkers(capacity), - as_cost: getCapacityInCost(capacity), - }, - })) - ), + of({ poll_interval: config.poll_interval }), + capacity$, ]).pipe( map((configurations) => ({ key: 'configuration', diff --git a/x-pack/platform/plugins/shared/task_manager/server/monitoring/index.ts b/x-pack/platform/plugins/shared/task_manager/server/monitoring/index.ts index fdcfe8aecebf1..6b0c163961def 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/monitoring/index.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/monitoring/index.ts @@ -15,7 +15,6 @@ import { } from './monitoring_stats_stream'; import { TaskStore } from '../task_store'; import { TaskPollingLifecycle } from '../polling_lifecycle'; -import { ManagedConfiguration } from '../lib/create_managed_configuration'; import { AdHocTaskCounter } from '../lib/adhoc_task_counter'; import { TaskTypeDictionary } from '../task_type_dictionary'; @@ -31,10 +30,10 @@ export interface CreateMonitoringStatsOpts { taskStore: TaskStore; elasticsearchAndSOAvailability$: Observable; config: TaskManagerConfig; - managedConfig: ManagedConfiguration; logger: Logger; adHocTaskCounter: AdHocTaskCounter; taskDefinitions: TaskTypeDictionary; + startingCapacity: number; taskPollingLifecycle?: TaskPollingLifecycle; } diff --git a/x-pack/platform/plugins/shared/task_manager/server/monitoring/monitoring_stats_stream.ts b/x-pack/platform/plugins/shared/task_manager/server/monitoring/monitoring_stats_stream.ts index b89f242741b05..8a8794b1d5e19 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/monitoring/monitoring_stats_stream.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/monitoring/monitoring_stats_stream.ts @@ -73,14 +73,14 @@ export function createAggregators({ taskStore, elasticsearchAndSOAvailability$, config, - managedConfig, logger, taskDefinitions, adHocTaskCounter, + startingCapacity, taskPollingLifecycle, }: CreateMonitoringStatsOpts): AggregatedStatProvider { const aggregators: AggregatedStatProvider[] = [ - createConfigurationAggregator(config, managedConfig), + createConfigurationAggregator(config, startingCapacity, taskPollingLifecycle), createWorkloadAggregator({ taskStore, diff --git a/x-pack/platform/plugins/shared/task_manager/server/plugin.ts b/x-pack/platform/plugins/shared/task_manager/server/plugin.ts index e8ed5aefbe6f9..fda9f34d4665a 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/plugin.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/plugin.ts @@ -35,7 +35,6 @@ import { removeIfExists } from './lib/remove_if_exists'; import { setupSavedObjects, BACKGROUND_TASK_NODE_SO_NAME, TASK_SO_NAME } from './saved_objects'; import { TaskDefinitionRegistry, TaskTypeDictionary } from './task_type_dictionary'; import { AggregationOpts, FetchResult, SearchOpts, TaskStore } from './task_store'; -import { createManagedConfiguration } from './lib/create_managed_configuration'; import { TaskScheduling } from './task_scheduling'; import { backgroundTaskUtilizationRoute, healthRoute, metricsRoute } from './routes'; import { createMonitoringStats, MonitoringStats } from './monitoring'; @@ -48,6 +47,7 @@ import { metricsStream, Metrics } from './metrics'; import { TaskManagerMetricsCollector } from './metrics/task_metrics_collector'; import { TaskPartitioner } from './lib/task_partitioner'; import { getDefaultCapacity } from './lib/get_default_capacity'; +import { calculateStartingCapacity } from './lib/create_managed_configuration'; import { registerMarkRemovedTasksAsUnrecognizedDefinition, scheduleMarkRemovedTasksAsUnrecognizedDefinition, @@ -325,12 +325,7 @@ export class TaskManagerPlugin }` ); - const managedConfiguration = createManagedConfiguration({ - config: this.config!, - errors$: taskStore.errors$, - defaultCapacity, - logger: this.logger, - }); + const startingCapacity = calculateStartingCapacity(this.config!, this.logger, defaultCapacity); // Only poll for tasks if configured to run tasks if (this.shouldRunBackgroundTasks) { @@ -357,8 +352,8 @@ export class TaskManagerPlugin usageCounter: this.usageCounter, middleware: this.middleware, elasticsearchAndSOAvailability$: this.elasticsearchAndSOAvailability$!, - ...managedConfiguration, taskPartitioner, + startingCapacity, }); } @@ -366,11 +361,11 @@ export class TaskManagerPlugin taskStore, elasticsearchAndSOAvailability$: this.elasticsearchAndSOAvailability$!, config: this.config!, - managedConfig: managedConfiguration, logger: this.logger, adHocTaskCounter: this.adHocTaskCounter, taskDefinitions: this.definitions, taskPollingLifecycle: this.taskPollingLifecycle, + startingCapacity, }).subscribe((stat) => this.monitoringStats$.next(stat)); metricsStream({ diff --git a/x-pack/platform/plugins/shared/task_manager/server/polling/delay_on_claim_conflicts.ts b/x-pack/platform/plugins/shared/task_manager/server/polling/delay_on_claim_conflicts.ts index 21b16b1a8d5c5..ccafc36fc7da7 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/polling/delay_on_claim_conflicts.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/polling/delay_on_claim_conflicts.ts @@ -15,7 +15,6 @@ import { merge, of, Observable, combineLatest, ReplaySubject } from 'rxjs'; import { filter, map } from 'rxjs'; import { Option, none, some, isSome, Some } from 'fp-ts/lib/Option'; import { isOk } from '../lib/result_type'; -import { ManagedConfiguration } from '../lib/create_managed_configuration'; import { TaskLifecycleEvent } from '../polling_lifecycle'; import { isTaskPollingCycleEvent } from '../task_events'; import { ClaimAndFillPoolResult } from '../lib/fill_pool'; @@ -26,8 +25,8 @@ import { getCapacityInWorkers } from '../task_pool'; * Emits a delay amount in ms to apply to polling whenever the task store exceeds a threshold of claim claimClashes */ export function delayOnClaimConflicts( - capacityConfiguration$: ManagedConfiguration['capacityConfiguration$'], - pollIntervalConfiguration$: ManagedConfiguration['pollIntervalConfiguration$'], + capacityConfiguration$: Observable, + pollIntervalConfiguration$: Observable, taskLifecycleEvents$: Observable, claimClashesPercentageThreshold: number, runningAverageWindowSize: number diff --git a/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.mock.ts b/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.mock.ts index 004729ca2b122..0453239da57c1 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.mock.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.mock.ts @@ -9,8 +9,15 @@ import { TaskPollingLifecycle, TaskLifecycleEvent } from './polling_lifecycle'; import { of, Observable } from 'rxjs'; export const taskPollingLifecycleMock = { - create(opts: { isStarted?: boolean; events$?: Observable }) { + create(opts: { + isStarted?: boolean; + events$?: Observable; + pollIntervalConfiguration$?: Observable; + capacityConfiguration$?: Observable; + }) { return { + pollIntervalConfiguration$: opts.pollIntervalConfiguration$ ?? of(), + capacityConfiguration$: opts.capacityConfiguration$ ?? of(), attemptToRun: jest.fn(), stop: jest.fn(), get isStarted() { diff --git a/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.test.ts b/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.test.ts index 1ccbe57debe24..e37764b78aaaf 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.test.ts @@ -6,7 +6,7 @@ */ import sinon from 'sinon'; -import { of, Subject } from 'rxjs'; +import { Subject } from 'rxjs'; import { TaskPollingLifecycle, claimAvailableTasks, TaskLifecycleEvent } from './polling_lifecycle'; import { createInitialMiddleware } from './lib/middleware'; @@ -105,8 +105,6 @@ describe('TaskPollingLifecycle', () => { definitions: new TaskTypeDictionary(taskManagerLogger), middleware: createInitialMiddleware(), startingCapacity: 20, - capacityConfiguration$: of(20), - pollIntervalConfiguration$: of(100), executionContext, taskPartitioner: new TaskPartitioner({ logger: taskManagerLogger, @@ -154,61 +152,35 @@ describe('TaskPollingLifecycle', () => { test('provides TaskClaiming with the capacity available when strategy = CLAIM_STRATEGY_UPDATE_BY_QUERY', () => { const elasticsearchAndSOAvailability$ = new Subject(); - const capacity$ = new Subject(); new TaskPollingLifecycle({ ...taskManagerOpts, elasticsearchAndSOAvailability$, - capacityConfiguration$: capacity$, + startingCapacity: 40, }); - const taskClaimingGetCapacity = (TaskClaiming as jest.Mock).mock .calls[0][0].getAvailableCapacity; - capacity$.next(40); expect(taskClaimingGetCapacity()).toEqual(40); expect(taskClaimingGetCapacity('report')).toEqual(1); expect(taskClaimingGetCapacity('quickReport')).toEqual(5); - - capacity$.next(60); - expect(taskClaimingGetCapacity()).toEqual(60); - expect(taskClaimingGetCapacity('report')).toEqual(1); - expect(taskClaimingGetCapacity('quickReport')).toEqual(5); - - capacity$.next(4); - expect(taskClaimingGetCapacity()).toEqual(4); - expect(taskClaimingGetCapacity('report')).toEqual(1); - expect(taskClaimingGetCapacity('quickReport')).toEqual(4); }); test('provides TaskClaiming with the capacity available when strategy = CLAIM_STRATEGY_MGET', () => { const elasticsearchAndSOAvailability$ = new Subject(); - const capacity$ = new Subject(); - new TaskPollingLifecycle({ ...taskManagerOpts, config: { ...taskManagerOpts.config, claim_strategy: CLAIM_STRATEGY_MGET }, elasticsearchAndSOAvailability$, - capacityConfiguration$: capacity$, + startingCapacity: 40, }); const taskClaimingGetCapacity = (TaskClaiming as jest.Mock).mock .calls[0][0].getAvailableCapacity; - capacity$.next(40); expect(taskClaimingGetCapacity()).toEqual(80); expect(taskClaimingGetCapacity('report')).toEqual(10); expect(taskClaimingGetCapacity('quickReport')).toEqual(10); - - capacity$.next(60); - expect(taskClaimingGetCapacity()).toEqual(120); - expect(taskClaimingGetCapacity('report')).toEqual(10); - expect(taskClaimingGetCapacity('quickReport')).toEqual(10); - - capacity$.next(4); - expect(taskClaimingGetCapacity()).toEqual(8); - expect(taskClaimingGetCapacity('report')).toEqual(8); - expect(taskClaimingGetCapacity('quickReport')).toEqual(8); }); }); @@ -591,6 +563,32 @@ describe('TaskPollingLifecycle', () => { }); }); }); + + describe('pollingLifecycleEvents capacity and poll interval', () => { + test('returns observables with initialized values', async () => { + const elasticsearchAndSOAvailability$ = new Subject(); + const taskPollingLifecycle = new TaskPollingLifecycle({ + ...taskManagerOpts, + config: { + ...taskManagerOpts.config, + poll_interval: 2, + }, + elasticsearchAndSOAvailability$, + }); + + elasticsearchAndSOAvailability$.next(true); + + const capacitySubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + + taskPollingLifecycle.capacityConfiguration$.subscribe(capacitySubscription); + taskPollingLifecycle.pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + expect(capacitySubscription).toHaveBeenCalledTimes(1); + expect(capacitySubscription).toHaveBeenNthCalledWith(1, 20); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + }); + }); }); type RetryableFunction = () => boolean; diff --git a/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.ts b/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.ts index 91f32d7201ea9..27914b1cf0241 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { Subject, Observable } from 'rxjs'; +import { Subject, Observable, withLatestFrom, BehaviorSubject } from 'rxjs'; +import { distinctUntilChanged, startWith } from 'rxjs'; import { pipe } from 'fp-ts/lib/pipeable'; import { map as mapOptional, none } from 'fp-ts/lib/Option'; import { tap } from 'rxjs'; @@ -13,8 +14,11 @@ import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import type { Logger, ExecutionContextStart } from '@kbn/core/server'; import { Result, asErr, mapErr, asOk, map, mapOk, isOk } from './lib/result_type'; -import { ManagedConfiguration } from './lib/create_managed_configuration'; -import { TaskManagerConfig, CLAIM_STRATEGY_UPDATE_BY_QUERY } from './config'; +import { + TaskManagerConfig, + CLAIM_STRATEGY_UPDATE_BY_QUERY, + WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW_SIZE_MS, +} from './config'; import { TaskMarkRunning, @@ -44,6 +48,13 @@ import { TaskClaiming } from './queries/task_claiming'; import { ClaimOwnershipResult } from './task_claimers'; import { TaskPartitioner } from './lib/task_partitioner'; import { TaskPoller } from './polling/task_poller'; +import { + createCapacityScan, + createPollIntervalScan, + countErrors, + ADJUST_THROUGHPUT_INTERVAL, +} from './lib/create_managed_configuration'; +import { createRunningAveragedStat } from './monitoring/task_run_calculators'; const MAX_BUFFER_OPERATIONS = 100; @@ -51,7 +62,7 @@ export interface ITaskEventEmitter { get events(): Observable; } -export type TaskPollingLifecycleOpts = { +export interface TaskPollingLifecycleOpts { logger: Logger; definitions: TaskTypeDictionary; taskStore: TaskStore; @@ -61,7 +72,8 @@ export type TaskPollingLifecycleOpts = { executionContext: ExecutionContextStart; usageCounter?: UsageCounter; taskPartitioner: TaskPartitioner; -} & ManagedConfiguration; + startingCapacity: number; +} export type TaskLifecycleEvent = | TaskMarkRunning @@ -88,6 +100,9 @@ export class TaskPollingLifecycle implements ITaskEventEmitter; + public pollIntervalConfiguration$: Observable; + // all task related events (task claimed, task marked as running, etc.) are emitted through events$ private events$ = new Subject(); @@ -96,6 +111,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter(0); /** * Initializes the task manager, preventing any further addition of middleware, @@ -105,8 +121,6 @@ export class TaskPollingLifecycle implements ITaskEventEmitter { - this.currentPollInterval = pollInterval; + const { poll_interval: pollInterval, claim_strategy: claimStrategy } = config; + this.currentPollInterval = pollInterval; + + const errorCheck$ = countErrors(taskStore.errors$, ADJUST_THROUGHPUT_INTERVAL); + const window = WORKER_UTILIZATION_RUNNING_AVERAGE_WINDOW_SIZE_MS / this.currentPollInterval; + const tmUtilizationQueue = createRunningAveragedStat(window); + this.capacityConfiguration$ = errorCheck$.pipe( + createCapacityScan(config, logger, startingCapacity), + startWith(startingCapacity), + distinctUntilChanged() + ); + this.pollIntervalConfiguration$ = errorCheck$.pipe( + withLatestFrom(this.currentTmUtilization$), + createPollIntervalScan(logger, this.currentPollInterval, claimStrategy, tmUtilizationQueue), + startWith(this.currentPollInterval), + distinctUntilChanged() + ); + this.pollIntervalConfiguration$.subscribe((newPollInterval) => { + this.currentPollInterval = newPollInterval; }); const emitEvent = (event: TaskLifecycleEvent) => this.events$.next(event); @@ -138,7 +169,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter | undefined; if (claimStrategy === CLAIM_STRATEGY_UPDATE_BY_QUERY) { pollIntervalDelay$ = delayOnClaimConflicts( - capacityConfiguration$, - pollIntervalConfiguration$, + this.capacityConfiguration$, + this.pollIntervalConfiguration$, this.events$, config.version_conflict_threshold, config.monitored_stats_running_average_window @@ -172,7 +201,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter({ logger, initialPollInterval: pollInterval, - pollInterval$: pollIntervalConfiguration$, + pollInterval$: this.pollIntervalConfiguration$, pollIntervalDelay$, getCapacity: () => { const capacity = this.pool.availableCapacity(); @@ -318,6 +347,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter; return mocked; }, diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/.storybook/decorator.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/.storybook/decorator.tsx index fcaf0ce7597ce..c568b69049c3f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/.storybook/decorator.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/.storybook/decorator.tsx @@ -72,6 +72,7 @@ export const StorybookContextDecorator: FC jest.mock('../../lib/rule_api/get_query_delay_settings', () => ({ getQueryDelaySettings: jest.fn(), })); +jest.mock('../../../common/get_experimental_features', () => ({ + getIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(false), +})); const queryClient = new QueryClient({ defaultOptions: { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.test.tsx index 06c9db5698d31..41d59bd74c90f 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.test.tsx @@ -33,6 +33,9 @@ jest.mock('../../lib/rule_api/get_query_delay_settings', () => ({ jest.mock('../../lib/rule_api/update_query_delay_settings', () => ({ updateQueryDelaySettings: jest.fn(), })); +jest.mock('../../../common/get_experimental_features', () => ({ + getIsExperimentalFeatureEnabled: jest.fn().mockReturnValue(false), +})); const queryClient = new QueryClient({ defaultOptions: { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.tsx index 09828e067369b..9071a4af55643 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/components/rules_setting/rules_settings_modal.tsx @@ -33,6 +33,7 @@ import { RulesSettingsQueryDelaySection } from './query_delay/rules_settings_que import { useGetQueryDelaySettings } from '../../hooks/use_get_query_delay_settings'; import { useUpdateRuleSettings } from '../../hooks/use_update_rules_settings'; import { CenterJustifiedSpinner } from '../center_justified_spinner'; +import { getIsExperimentalFeatureEnabled } from '../../../common/get_experimental_features'; export const RulesSettingsErrorPrompt = memo(() => { return ( @@ -219,6 +220,9 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => { if (isFlappingLoading || isQueryDelayLoading) { return ; } + const isAlertDeletionSettingsEnabled = getIsExperimentalFeatureEnabled( + 'alertDeletionSettingsEnabled' + ); return ( <> {flappingSettings && ( @@ -230,6 +234,7 @@ export const RulesSettingsModal = memo((props: RulesSettingsModalProps) => { hasError={hasFlappingError} /> )} + {isAlertDeletionSettingsEnabled &&
Alert Deletion Settings Placeholder
} {isServerless && queryDelaySettings && ( <> diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_experimental_features.test.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_experimental_features.test.tsx index b33622423b92d..b81ed7606f744 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_experimental_features.test.tsx +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/common/get_experimental_features.test.tsx @@ -25,6 +25,7 @@ describe('getIsExperimentalFeatureEnabled', () => { isMustacheAutocompleteOn: false, showMustacheAutocompleteSwitch: false, isUsingRuleCreateFlyout: false, + alertDeletionSettingsEnabled: false, }, }); diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/api.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/api.ts index d75cce345a1b0..40ab0117c1772 100644 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/api.ts +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/api.ts @@ -7,13 +7,55 @@ import { z } from '@kbn/zod'; import { + ingestStreamGetResponseSchema, ingestStreamUpsertRequestSchema, + unwiredStreamGetResponseSchema, + wiredStreamGetResponseSchema, type IngestStreamGetResponse, type IngestStreamUpsertRequest, } from './ingest'; +import { + GroupStreamGetResponse, + groupStreamGetResponseSchema, + GroupStreamUpsertRequest, + groupStreamUpsertRequestSchema, +} from './group'; +import { createAsSchemaOrThrow, createIsNarrowSchema } from '../helpers'; + +export const streamGetResponseSchema: z.Schema = z.union([ + ingestStreamGetResponseSchema, + groupStreamGetResponseSchema, +]); + +export const streamUpsertRequestSchema: z.Schema = z.union([ + ingestStreamUpsertRequestSchema, + groupStreamUpsertRequestSchema, +]); + +export const isWiredStreamGetResponse = createIsNarrowSchema( + streamGetResponseSchema, + wiredStreamGetResponseSchema +); + +export const isUnwiredStreamGetResponse = createIsNarrowSchema( + streamGetResponseSchema, + unwiredStreamGetResponseSchema +); + +export const asWiredStreamGetResponse = createAsSchemaOrThrow( + streamGetResponseSchema, + wiredStreamGetResponseSchema +); + +export const asUnwiredStreamGetResponse = createAsSchemaOrThrow( + streamGetResponseSchema, + unwiredStreamGetResponseSchema +); -export const streamUpsertRequestSchema: z.Schema = - ingestStreamUpsertRequestSchema; +export const asIngestStreamGetResponse = createAsSchemaOrThrow( + streamGetResponseSchema, + ingestStreamGetResponseSchema +); -export type StreamGetResponse = IngestStreamGetResponse; -export type StreamUpsertRequest = IngestStreamUpsertRequest; +export type StreamGetResponse = IngestStreamGetResponse | GroupStreamGetResponse; +export type StreamUpsertRequest = IngestStreamUpsertRequest | GroupStreamUpsertRequest; diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/core.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/core.ts index 477eb7711218b..b77ed58d4c4d1 100644 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/core.ts +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/core.ts @@ -8,9 +8,13 @@ import { z } from '@kbn/zod'; import { createIsNarrowSchema } from '../helpers'; import { IngestStreamDefinition, ingestStreamDefinitionSchema } from './ingest'; +import { GroupStreamDefinition, groupStreamDefinitionSchema } from './group'; -export type StreamDefinition = IngestStreamDefinition; +export type StreamDefinition = IngestStreamDefinition | GroupStreamDefinition; -export const streamDefinitionSchema: z.Schema = ingestStreamDefinitionSchema; +export const streamDefinitionSchema: z.Schema = z.union([ + ingestStreamDefinitionSchema, + groupStreamDefinitionSchema, +]); export const isStreamDefinition = createIsNarrowSchema(z.unknown(), streamDefinitionSchema); diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/api.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/api.ts new file mode 100644 index 0000000000000..0d129c0e1e995 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/api.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 { z } from '@kbn/zod'; +import { + StreamGetResponseBase, + streamGetResponseSchemaBase, + StreamUpsertRequestBase, + streamUpsertRequestSchemaBase, +} from '../base/api'; +import { GroupStreamDefinitionBase, groupStreamDefinitionBaseSchema } from './base'; + +/** + * Group get response + */ +interface GroupStreamGetResponse extends StreamGetResponseBase { + stream: GroupStreamDefinitionBase; +} + +const groupStreamGetResponseSchema: z.Schema = z.intersection( + streamGetResponseSchemaBase, + z.object({ + stream: groupStreamDefinitionBaseSchema, + }) +); + +/** + * Group upsert request + */ +interface GroupStreamUpsertRequest extends StreamUpsertRequestBase { + stream: GroupStreamDefinitionBase; +} + +const groupStreamUpsertRequestSchema: z.Schema = z.intersection( + streamUpsertRequestSchemaBase, + z.object({ + stream: groupStreamDefinitionBaseSchema, + }) +); + +export { + type GroupStreamGetResponse, + type GroupStreamUpsertRequest, + groupStreamGetResponseSchema, + groupStreamUpsertRequestSchema, +}; diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/base.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/base.ts new file mode 100644 index 0000000000000..ad9ee05b58d19 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/base.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from '@kbn/zod'; +import { NonEmptyString } from '@kbn/zod-helpers'; +import { StreamDefinitionBase } from '../base'; + +interface GroupBase { + description?: string; + members: string[]; +} + +const groupBaseSchema: z.Schema = z.object({ + description: z.optional(z.string()), + members: z.array(NonEmptyString), +}); + +interface GroupStreamDefinitionBase { + group: GroupBase; +} + +const groupStreamDefinitionBaseSchema: z.Schema = z.object({ + group: groupBaseSchema, +}); + +type GroupStreamDefinition = StreamDefinitionBase & GroupStreamDefinitionBase; + +const groupStreamDefinitionSchema: z.Schema = z.intersection( + z.object({ name: NonEmptyString }), + groupStreamDefinitionBaseSchema +); + +export { + type GroupBase, + type GroupStreamDefinitionBase, + type GroupStreamDefinition, + groupBaseSchema, + groupStreamDefinitionBaseSchema, + groupStreamDefinitionSchema, +}; diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/index.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/index.ts new file mode 100644 index 0000000000000..145a9ec410405 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/group/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './base'; +export * from './api'; diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/helpers.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/helpers.ts index 6330cab2a134d..a9a5e7ef24ffc 100644 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/helpers.ts +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/helpers.ts @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { createIsNarrowSchema } from '../helpers'; +import { z } from '@kbn/zod'; +import { createAsSchemaOrThrow, createIsNarrowSchema } from '../helpers'; import { streamDefinitionSchema } from './core'; +import { groupStreamDefinitionBaseSchema, groupStreamDefinitionSchema } from './group'; import { ingestStreamDefinitionSchema, unwiredStreamDefinitionSchema, @@ -23,11 +24,31 @@ export const isWiredStreamDefinition = createIsNarrowSchema( wiredStreamDefinitionSchema ); +export const asIngestStreamDefinition = createAsSchemaOrThrow( + streamDefinitionSchema, + ingestStreamDefinitionSchema +); + +export const asWiredStreamDefinition = createAsSchemaOrThrow( + streamDefinitionSchema, + wiredStreamDefinitionSchema +); + export const isUnwiredStreamDefinition = createIsNarrowSchema( streamDefinitionSchema, unwiredStreamDefinitionSchema ); +export const isGroupStreamDefinition = createIsNarrowSchema( + streamDefinitionSchema, + groupStreamDefinitionSchema +); + +export const isGroupStreamDefinitionBase = createIsNarrowSchema( + z.unknown(), + groupStreamDefinitionBaseSchema +); + export const isRootStreamDefinition = createIsNarrowSchema( streamDefinitionSchema, wiredStreamDefinitionSchema.refine((stream) => { diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/index.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/index.ts index d4d6276fee51b..be9dafc266b3a 100644 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/index.ts +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/index.ts @@ -6,8 +6,8 @@ */ export * from './ingest'; -export * from './legacy'; export * from './api'; export * from './core'; export * from './helpers'; +export * from './group'; diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/api.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/api.ts index 0803c848ad663..0a4231c64cfe2 100644 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/api.ts +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/api.ts @@ -26,7 +26,6 @@ import { wiredStreamDefinitionSchemaBase, } from './base'; import { ElasticsearchAsset, elasticsearchAssetSchema } from './common'; -import { createIsNarrowSchema, createAsSchemaOrThrow } from '../../helpers'; import { UnwiredIngestStreamEffectiveLifecycle, WiredIngestStreamEffectiveLifecycle, @@ -146,33 +145,12 @@ const ingestStreamGetResponseSchema: z.Schema = z.union unwiredStreamGetResponseSchema, ]); -const isWiredStreamGetResponse = createIsNarrowSchema( - ingestStreamGetResponseSchema, - wiredStreamGetResponseSchema -); - -const isUnWiredStreamGetResponse = createIsNarrowSchema( - ingestStreamGetResponseSchema, - unwiredStreamGetResponseSchema -); - -const asWiredStreamGetResponse = createAsSchemaOrThrow( - ingestStreamGetResponseSchema, - wiredStreamGetResponseSchema -); - -const asUnwiredStreamGetResponse = createAsSchemaOrThrow( - ingestStreamGetResponseSchema, - unwiredStreamGetResponseSchema -); - export { ingestStreamUpsertRequestSchema, ingestUpsertRequestSchema, - isWiredStreamGetResponse, - isUnWiredStreamGetResponse, - asWiredStreamGetResponse, - asUnwiredStreamGetResponse, + ingestStreamGetResponseSchema, + wiredStreamGetResponseSchema, + unwiredStreamGetResponseSchema, type IngestGetResponse, type IngestStreamGetResponse, type IngestStreamUpsertRequest, diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/processors/index.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/processors/index.ts index 4eae056b45f05..2c5e18e0487fb 100644 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/processors/index.ts +++ b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/ingest/processors/index.ts @@ -11,14 +11,15 @@ import { Condition, conditionSchema } from '../conditions'; import { createIsNarrowSchema } from '../../../helpers'; export interface ProcessorBase { + description?: string; if: Condition; + ignore_failure?: boolean; } export interface GrokProcessorConfig extends ProcessorBase { field: string; patterns: string[]; pattern_definitions?: Record; - ignore_failure?: boolean; ignore_missing?: boolean; } @@ -27,7 +28,9 @@ export interface GrokProcessorDefinition { } const processorBaseSchema = z.object({ + description: z.optional(z.string()), if: conditionSchema, + ignore_failure: z.optional(z.boolean()), }); export const grokProcessorDefinitionSchema: z.Schema = z.strictObject({ @@ -35,9 +38,8 @@ export const grokProcessorDefinitionSchema: z.Schema = processorBaseSchema, z.object({ field: NonEmptyString, - patterns: z.array(NonEmptyString), + patterns: z.array(NonEmptyString).nonempty(), pattern_definitions: z.optional(z.record(z.string())), - ignore_failure: z.optional(z.boolean()), ignore_missing: z.optional(z.boolean()), }) ), @@ -47,7 +49,6 @@ export interface DissectProcessorConfig extends ProcessorBase { field: string; pattern: string; append_separator?: string; - ignore_failure?: boolean; ignore_missing?: boolean; } @@ -63,7 +64,6 @@ export const dissectProcessorDefinitionSchema: z.Schema; export type ProcessorType = UnionKeysOf; -type ProcessorTypeOf = +export type ProcessorTypeOf = UnionKeysOf & ProcessorType; export const processorDefinitionSchema: z.ZodType = z.union([ @@ -96,15 +96,20 @@ export const isDissectProcessorDefinition = createIsNarrowSchema( dissectProcessorDefinitionSchema ); +const processorTypes: ProcessorType[] = (processorDefinitionSchema as z.ZodUnion).options.map( + (option: z.ZodUnion['options'][number]) => Object.keys(option.shape)[0] +); + export function getProcessorType( processor: TProcessorDefinition ): ProcessorTypeOf { - return Object.keys(processor)[0] as ProcessorTypeOf; + return processorTypes.find((type) => type in processor) as ProcessorTypeOf; } -export function getProcessorConfig(processor: ProcessorDefinition): ProcessorConfig { - if ('grok' in processor) { - return processor.grok; - } - return processor.dissect; +export function getProcessorConfig( + processor: TProcessorDefinition +): ProcessorConfig { + const type = getProcessorType(processor); + + return processor[type as keyof TProcessorDefinition]; } diff --git a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/legacy.ts b/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/legacy.ts deleted file mode 100644 index 3415326edcbf2..0000000000000 --- a/x-pack/solutions/observability/packages/kbn-streams-schema/src/models/legacy.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { z } from '@kbn/zod'; -import { NonEmptyString } from '@kbn/zod-helpers'; -import { - InheritedFieldDefinition, - UnwiredIngestStreamEffectiveLifecycle, - UnwiredStreamDefinition, - WiredIngestStreamEffectiveLifecycle, - WiredStreamDefinition, - inheritedFieldDefinitionSchema, - unwiredIngestStreamEffectiveLifecycleSchema, - unwiredStreamDefinitionSchema, - wiredIngestStreamEffectiveLifecycleSchema, - wiredStreamDefinitionSchema, -} from './ingest'; -import { ElasticsearchAsset, elasticsearchAssetSchema } from './ingest/common'; -import { createIsNarrowSchema } from '../helpers'; - -/** - * These are deprecated types, they should be migrated to the updated types - */ - -interface ReadStreamDefinitionBase { - name: string; - dashboards: string[]; - elasticsearch_assets: ElasticsearchAsset[]; - inherited_fields: InheritedFieldDefinition; -} - -interface WiredReadStreamDefinition extends ReadStreamDefinitionBase { - stream: WiredStreamDefinition; - effective_lifecycle: WiredIngestStreamEffectiveLifecycle; -} - -interface UnwiredReadStreamDefinition extends ReadStreamDefinitionBase { - stream: UnwiredStreamDefinition; - data_stream_exists: boolean; - effective_lifecycle: UnwiredIngestStreamEffectiveLifecycle; -} - -type ReadStreamDefinition = WiredReadStreamDefinition | UnwiredReadStreamDefinition; - -const readStreamDefinitionSchemaBase: z.Schema = z.object({ - name: z.string(), - dashboards: z.array(NonEmptyString), - elasticsearch_assets: z.array(elasticsearchAssetSchema), - inherited_fields: inheritedFieldDefinitionSchema, -}); - -const wiredReadStreamDefinitionSchema: z.Schema = z.intersection( - readStreamDefinitionSchemaBase, - z.object({ - stream: wiredStreamDefinitionSchema, - effective_lifecycle: wiredIngestStreamEffectiveLifecycleSchema, - }) -); - -const unwiredReadStreamDefinitionSchema: z.Schema = z.intersection( - readStreamDefinitionSchemaBase, - z.object({ - stream: unwiredStreamDefinitionSchema, - data_stream_exists: z.boolean(), - effective_lifecycle: unwiredIngestStreamEffectiveLifecycleSchema, - }) -); - -const readStreamSchema: z.Schema = z.union([ - wiredReadStreamDefinitionSchema, - unwiredReadStreamDefinitionSchema, -]); - -const isReadStream = createIsNarrowSchema(z.unknown(), readStreamSchema); -const isWiredReadStream = createIsNarrowSchema(readStreamSchema, wiredReadStreamDefinitionSchema); -const isUnwiredReadStream = createIsNarrowSchema( - readStreamSchema, - unwiredReadStreamDefinitionSchema -); - -export { - readStreamSchema, - type ReadStreamDefinition, - type WiredReadStreamDefinition, - type UnwiredReadStreamDefinition, - isReadStream, - isWiredReadStream, - isUnwiredReadStream, - wiredReadStreamDefinitionSchema, -}; diff --git a/x-pack/solutions/observability/packages/synthetics_test_data/kibana.jsonc b/x-pack/solutions/observability/packages/synthetics_test_data/kibana.jsonc index 74e5bd7a76c19..a3e8cf02c7e4d 100644 --- a/x-pack/solutions/observability/packages/synthetics_test_data/kibana.jsonc +++ b/x-pack/solutions/observability/packages/synthetics_test_data/kibana.jsonc @@ -3,5 +3,6 @@ "id": "@kbn/observability-synthetics-test-data", "owner": "@elastic/obs-ux-management-team", "group": "observability", - "visibility": "private" + "visibility": "private", + "devOnly": true } diff --git a/x-pack/solutions/observability/packages/utils_server/es/storage/index.ts b/x-pack/solutions/observability/packages/utils_server/es/storage/index.ts index fde2e0fa966b1..d2051b720dfe2 100644 --- a/x-pack/solutions/observability/packages/utils_server/es/storage/index.ts +++ b/x-pack/solutions/observability/packages/utils_server/es/storage/index.ts @@ -52,7 +52,7 @@ export type StorageClientBulkOperation = } | { delete: { _id: string } }; -export type StorageClientBulkRequest> = Omit< +export type StorageClientBulkRequest = Omit< BulkRequest, 'operations' | 'index' > & { @@ -80,21 +80,20 @@ export type StorageClientIndexRequest = Omit< export type StorageClientIndexResponse = IndexResponse; export type StorageClientGetRequest = Omit; -export type StorageClientGetResponse> = - GetResponse; +export type StorageClientGetResponse = GetResponse; -export type StorageClientSearch = < +export type StorageClientSearch = < TSearchRequest extends StorageClientSearchRequest >( request: TSearchRequest -) => Promise, TSearchRequest>>; +) => Promise>; -export type StorageClientBulk = ( - request: StorageClientBulkRequest> +export type StorageClientBulk = ( + request: StorageClientBulkRequest ) => Promise; -export type StorageClientIndex = ( - request: StorageClientIndexRequest> +export type StorageClientIndex = ( + request: StorageClientIndexRequest ) => Promise; export type StorageClientDelete = ( @@ -103,22 +102,50 @@ export type StorageClientDelete = ( export type StorageClientClean = () => Promise; -export type StorageClientGet = ( +export type StorageClientGet = ( request: StorageClientGetRequest -) => Promise>>; +) => Promise>; export type StorageClientExistsIndex = () => Promise; -export interface IStorageClient { - search: StorageClientSearch; - bulk: StorageClientBulk; - index: StorageClientIndex; +export interface InternalIStorageClient { + search: StorageClientSearch; + bulk: StorageClientBulk; + index: StorageClientIndex; delete: StorageClientDelete; clean: StorageClientClean; - get: StorageClientGet; + get: StorageClientGet; existsIndex: StorageClientExistsIndex; } +type UnionKeys = T extends T ? keyof T : never; +type Exact = T extends U + ? Exclude, UnionKeys> extends never + ? true + : false + : false; + +// The storage settings need to support the application payload type, but it's OK if the +// storage document can hold more fields than the application document. +// To keep the type safety of the application type in the consuming code, both the storage +// settings and the application type are passed to the IStorageClient type. +// The IStorageClient type then checks if the application type is a subset of the storage +// document type. If this is not the case, the IStorageClient type is set to never, which +// will cause a type error in the consuming code. +export type IStorageClient = Exact< + ApplicationDocument, + Partial> +> extends true + ? InternalIStorageClient> + : never; + +export type SimpleIStorageClient = IStorageClient< + TStorageSettings, + Omit, '_id'> +>; + +export type ApplicationDocument = TApplicationType & { _id: string }; + export type StorageDocumentOf = StorageFieldTypeOf<{ type: 'object'; properties: TStorageSettings['schema']['properties']; diff --git a/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/index.ts b/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/index.ts index ac35a0aaf3dad..233f6797c9092 100644 --- a/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/index.ts +++ b/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/index.ts @@ -27,13 +27,14 @@ import { StorageClientIndex, StorageClientIndexResponse, StorageClientSearch, - IStorageClient, StorageClientGet, StorageClientExistsIndex, StorageDocumentOf, StorageClientSearchResponse, StorageClientClean, StorageClientCleanResponse, + ApplicationDocument, + InternalIStorageClient, } from '..'; import { getSchemaVersion } from '../get_schema_version'; import { StorageMappingProperty } from '../types'; @@ -94,7 +95,7 @@ function wrapEsCall(p: Promise): Promise { * - Index Lifecycle Management * - Schema upgrades w/ fallbacks */ -export class StorageIndexAdapter { +export class StorageIndexAdapter { private readonly logger: Logger; constructor( private readonly esClient: ElasticsearchClient, @@ -316,7 +317,7 @@ export class StorageIndexAdapter return []; } - private search: StorageClientSearch = async (request) => { + private search: StorageClientSearch> = async (request) => { return (await wrapEsCall( this.esClient .search({ @@ -345,10 +346,10 @@ export class StorageIndexAdapter } throw error; }) - )) as unknown as ReturnType>; + )) as unknown as ReturnType>>; }; - private index: StorageClientIndex = async ({ + private index: StorageClientIndex> = async ({ id, refresh = 'wait_for', ...request @@ -387,7 +388,7 @@ export class StorageIndexAdapter }); }; - private bulk: StorageClientBulk = ({ + private bulk: StorageClientBulk> = ({ operations, refresh = 'wait_for', ...request @@ -402,7 +403,7 @@ export class StorageIndexAdapter _id: operation.index._id, }, }, - operation.index.document, + operation.index.document as {}, ]; } @@ -518,7 +519,10 @@ export class StorageIndexAdapter return { acknowledged: true, result: 'not_found' }; }; - private get: StorageClientGet = async ({ id, ...request }) => { + private get: StorageClientGet> = async ({ + id, + ...request + }) => { const response = await this.search({ track_total_hits: false, size: 1, @@ -558,7 +562,7 @@ export class StorageIndexAdapter _id: hit._id!, _index: hit._index, found: true, - _source: hit._source as StorageDocumentOf, + _source: hit._source as ApplicationDocument, _ignored: hit._ignored, _primary_term: hit._primary_term, _routing: hit._routing, @@ -574,7 +578,7 @@ export class StorageIndexAdapter }); }; - getClient(): IStorageClient { + getClient(): InternalIStorageClient> { return { bulk: this.bulk, delete: this.delete, @@ -586,3 +590,6 @@ export class StorageIndexAdapter }; } } + +export type SimpleStorageIndexAdapter = + StorageIndexAdapter, '_id'>>; diff --git a/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/integration_tests/index.test.ts b/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/integration_tests/index.test.ts index 4574a777a1b6a..4b9327d4cc5c8 100644 --- a/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/integration_tests/index.test.ts +++ b/x-pack/solutions/observability/packages/utils_server/es/storage/index_adapter/integration_tests/index.test.ts @@ -11,7 +11,7 @@ import { type TestKibanaUtils, } from '@kbn/core-test-helpers-kbn-server'; import { - IStorageClient, + SimpleIStorageClient, StorageClientBulkResponse, StorageClientIndexResponse, StorageIndexAdapter, @@ -22,6 +22,7 @@ import { httpServerMock } from '@kbn/core/server/mocks'; import * as getSchemaVersionModule from '../../get_schema_version'; import { isResponseError } from '@kbn/es-errors'; import { IndicesGetResponse } from '@elastic/elasticsearch/lib/api/types'; +import { SimpleStorageIndexAdapter } from '..'; const TEST_INDEX_NAME = 'test_index'; @@ -56,8 +57,8 @@ describe('StorageIndexAdapter', () => { }, } satisfies StorageSettings; - let adapter: StorageIndexAdapter; - let client: IStorageClient; + let adapter: SimpleStorageIndexAdapter; + let client: SimpleIStorageClient; describe('with a clean Elasticsearch instance', () => { beforeAll(async () => { @@ -406,7 +407,7 @@ describe('StorageIndexAdapter', () => { function createStorageIndexAdapter( settings: TStorageSettings - ): StorageIndexAdapter { + ): SimpleStorageIndexAdapter { return new StorageIndexAdapter(esClient, loggerMock, settings); } diff --git a/x-pack/solutions/observability/packages/utils_server/es/storage/types.ts b/x-pack/solutions/observability/packages/utils_server/es/storage/types.ts index c13487a4d2abf..08d8fecff3cf0 100644 --- a/x-pack/solutions/observability/packages/utils_server/es/storage/types.ts +++ b/x-pack/solutions/observability/packages/utils_server/es/storage/types.ts @@ -91,7 +91,7 @@ type PrimitiveOf = { float: number; object: TProperty extends { properties: Record } ? { - [key in keyof TProperty['properties']]: StorageFieldTypeOf; + [key in keyof TProperty['properties']]?: StorageFieldTypeOf; } : object; }[TProperty['type']]; diff --git a/x-pack/solutions/observability/plugins/apm/common/tutorial/instructions/apm_agent_instructions.ts b/x-pack/solutions/observability/plugins/apm/common/tutorial/instructions/apm_agent_instructions.ts index c4b91a0f978c0..96e94058325d4 100644 --- a/x-pack/solutions/observability/plugins/apm/common/tutorial/instructions/apm_agent_instructions.ts +++ b/x-pack/solutions/observability/plugins/apm/common/tutorial/instructions/apm_agent_instructions.ts @@ -384,21 +384,22 @@ export const createDotNetAgentInstructions = (apmServerUrl = '', secretToken = ' }), textPre: i18n.translate('xpack.apm.tutorial.dotNetClient.configureApplication.textPre', { defaultMessage: - 'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `UseAllElasticApm` \ - method in the `Configure` method within the `Startup.cs` file.', - }), - commands: `public class Startup -{curlyOpen} - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - {curlyOpen} - app.UseAllElasticApm(Configuration); - //…rest of the method - {curlyClose} - //…rest of the class -{curlyClose}`.split('\n'), + 'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `AddAllElasticApm` \ + extension method on the `IServiceCollection` available via the `WebApplicationBuilder` \ + within the `Program.cs` file.', + }), + commands: `var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddAllElasticApm(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +app.Run();`.split('\n'), textPost: i18n.translate('xpack.apm.tutorial.dotNetClient.configureApplication.textPost', { defaultMessage: - 'Passing an `IConfiguration` instance is optional and by doing so, the agent will read config settings through this \ + 'The agent will implicitly read config settings from the applications \ `IConfiguration` instance (e.g. from the `appsettings.json` file).', }), }, @@ -409,8 +410,7 @@ export const createDotNetAgentInstructions = (apmServerUrl = '', secretToken = ' customComponentName: 'TutorialConfigAgent', textPost: i18n.translate('xpack.apm.tutorial.dotNetClient.configureAgent.textPost', { defaultMessage: - 'In case you don’t pass an `IConfiguration` instance to the agent (e.g. in case of non ASP.NET Core applications) \ - you can also configure the agent through environment variables. \n \ + 'You can also configure the agent through environment variables. \n \ See [the documentation]({documentationLink}) for advanced usage, including the [Profiler Auto instrumentation]({profilerLink}) quick start.', values: { documentationLink: diff --git a/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_map/service_map.cy.ts b/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_map/service_map.cy.ts index ab6afb310623a..b1c868b6e99a3 100644 --- a/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_map/service_map.cy.ts +++ b/x-pack/solutions/observability/plugins/apm/ftr_e2e/cypress/e2e/service_map/service_map.cy.ts @@ -29,7 +29,8 @@ const detailedServiceMap = url.format({ }, }); -describe('service map', () => { +// Flaky: https://github.com/elastic/kibana/issues/207005 +describe.skip('service map', () => { before(() => { synthtrace.index( opbeans({ @@ -52,7 +53,7 @@ describe('service map', () => { cy.intercept('GET', '/internal/apm/service-map?*').as('serviceMap'); }); - it.skip('shows nodes in service map', () => { + it('shows nodes in service map', () => { cy.visitKibana(serviceMapHref); cy.wait('@serviceMap'); @@ -68,7 +69,7 @@ describe('service map', () => { ); }); - it.skip('shows nodes in detailed service map', () => { + it('shows nodes in detailed service map', () => { cy.visitKibana(detailedServiceMap); cy.wait('@serviceMap'); cy.contains('h1', 'opbeans-java'); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/dependencies_inventory/dependencies_inventory_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/dependencies_inventory/dependencies_inventory_table/index.tsx index 720703c2f52bd..1d007f67921e4 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/dependencies_inventory/dependencies_inventory_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/dependencies_inventory/dependencies_inventory_table/index.tsx @@ -7,10 +7,11 @@ import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useEffect } from 'react'; import { useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { useUiTracker } from '@kbn/observability-shared-plugin/public'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { isTimeComparison } from '../../../shared/time_comparison/get_comparison_options'; import { getNodeName, NodeType } from '../../../../../common/connections'; import { useApmParams } from '../../../../hooks/use_apm_params'; @@ -24,7 +25,7 @@ export function DependenciesInventoryTable() { const { query: { rangeFrom, rangeTo, environment, kuery, comparisonEnabled, offset }, } = useApmParams('/dependencies/inventory'); - + const { onPageReady } = usePerformanceContext(); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); const { euiTheme } = useEuiTheme(); @@ -52,6 +53,17 @@ export function DependenciesInventoryTable() { [start, end, environment, offset, kuery, comparisonEnabled] ); + useEffect(() => { + if (status === FETCH_STATUS.SUCCESS) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + }, [status, onPageReady, rangeFrom, rangeTo]); + const dependencies = data?.dependencies.map((dependency) => { const { location } = dependency; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx index eb3211ff4798d..6ebacce7d7853 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_operations/dependency_detail_operations_list/index.tsx @@ -7,7 +7,8 @@ import { i18n } from '@kbn/i18n'; import { keyBy } from 'lodash'; -import React from 'react'; +import React, { useEffect } from 'react'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; import { useSearchServiceDestinationMetrics } from '../../../../context/time_range_metadata/use_search_service_destination_metrics'; import { useApmParams } from '../../../../hooks/use_apm_params'; @@ -59,7 +60,7 @@ export function DependencyDetailOperationsList() { offset, }, } = useApmParams('/dependencies/operations'); - + const { onPageReady } = usePerformanceContext(); const { core } = useApmPluginContext(); const { isLarge } = useBreakpoints(); @@ -132,6 +133,20 @@ export function DependencyDetailOperationsList() { ] ); + useEffect(() => { + if ( + comparisonStatsFetch.status === FETCH_STATUS.SUCCESS && + primaryStatsFetch.status === FETCH_STATUS.SUCCESS + ) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + }, [onPageReady, primaryStatsFetch, comparisonStatsFetch, rangeFrom, rangeTo]); + const columns: Array> = [ { name: i18n.translate('xpack.apm.dependencyDetailOperationsList.spanNameColumnLabel', { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_overview/dependencies_detail_table.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_overview/dependencies_detail_table.tsx index d910495b5a6d4..c332ad4895af0 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_overview/dependencies_detail_table.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/dependency_detail_overview/dependencies_detail_table.tsx @@ -6,11 +6,12 @@ */ import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useEffect } from 'react'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { isTimeComparison } from '../../shared/time_comparison/get_comparison_options'; import { getNodeName, NodeType } from '../../../../common/connections'; import { useApmParams } from '../../../hooks/use_apm_params'; -import { useFetcher } from '../../../hooks/use_fetcher'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { DependenciesTable } from '../../shared/dependencies_table'; import { ServiceLink } from '../../shared/links/apm/service_link'; import { useTimeRange } from '../../../hooks/use_time_range'; @@ -31,6 +32,7 @@ export function DependenciesDetailTable() { } = useApmParams('/dependencies/overview'); const { core } = useApmPluginContext(); + const { onPageReady } = usePerformanceContext(); const comparisonEnabled = getComparisonEnabled({ core, @@ -58,6 +60,17 @@ export function DependenciesDetailTable() { [start, end, environment, offset, dependencyName, kuery, comparisonEnabled] ); + useEffect(() => { + if (status === FETCH_STATUS.SUCCESS) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + }, [onPageReady, status, rangeFrom, rangeTo]); + const dependencies = data?.services.map((dependency) => { const { location } = dependency; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/index.tsx index 64bc6f8bb9f6f..c19f20c6ab26a 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_details/index.tsx @@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect } from 'react'; import { omit } from 'lodash'; import { useHistory } from 'react-router-dom'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { isOpenTelemetryAgentName, isRumAgentName } from '../../../../common/agent_name'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; @@ -82,7 +83,7 @@ export function ErrorGroupDetails() { const apmRouter = useApmRouter(); const history = useHistory(); - + const { onPageReady } = usePerformanceContext(); const { observabilityAIAssistant } = useApmPluginContext(); const { @@ -170,6 +171,20 @@ export function ErrorGroupDetails() { } }, [history, errorId, errorSamplesData, errorSamplesFetchStatus]); + useEffect(() => { + if ( + errorSamplesFetchStatus === FETCH_STATUS.SUCCESS && + errorDistributionStatus === FETCH_STATUS.SUCCESS + ) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + }, [onPageReady, errorSamplesFetchStatus, errorDistributionStatus, rangeFrom, rangeTo]); + const { agentName } = useApmServiceContext(); const isOpenTelemetryAgent = isOpenTelemetryAgentName(agentName as AgentName); const isRumAgent = isRumAgentName(agentName as AgentName); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.test.tsx index 278825c25c68c..4d97f06338daf 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.test.tsx @@ -10,6 +10,13 @@ import { render } from '@testing-library/react'; import React from 'react'; import * as stories from './error_group_list.stories'; +// Mock the usePerformanceContext hook +jest.mock('@kbn/ebt-tools', () => ({ + usePerformanceContext: () => ({ + onPageReady: jest.fn(), + }), +})); + const { Example } = composeStories(stories); describe('ErrorGroupList', () => { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx index 218f007378970..32c69c90d0d2d 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx @@ -8,9 +8,10 @@ import { EuiBadge, EuiIconTip, EuiToolTip, RIGHT_ALIGNMENT } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import styled from '@emotion/styled'; -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState, useRef } from 'react'; import { apmEnableTableSearchBar } from '@kbn/observability-plugin/common'; -import { isPending } from '../../../../hooks/use_fetcher'; +import { usePerformanceContext } from '@kbn/ebt-tools'; +import { FETCH_STATUS, isPending } from '../../../../hooks/use_fetcher'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { asBigNumber } from '../../../../../common/utils/formatters'; import { useAnyOfApmParams } from '../../../../hooks/use_apm_params'; @@ -55,6 +56,7 @@ interface Props { comparisonEnabled?: boolean; saveTableOptionsToUrl?: boolean; showPerPageOptions?: boolean; + onLoadTable?: () => void; } const defaultSorting = { @@ -69,6 +71,7 @@ export function ErrorGroupList({ comparisonEnabled, saveTableOptionsToUrl, showPerPageOptions = true, + onLoadTable, }: Props) { const { query } = useAnyOfApmParams( '/services/{serviceName}/overview', @@ -79,10 +82,10 @@ export function ErrorGroupList({ const isTableSearchBarEnabled = core.uiSettings.get(apmEnableTableSearchBar, true); - const { offset } = query; + const { offset, rangeFrom, rangeTo } = query; const [renderedItems, setRenderedItems] = useState([]); - + const hasTableLoaded = useRef(false); const [sorting, setSorting] = useState['sort']>(defaultSorting); const { @@ -95,6 +98,36 @@ export function ErrorGroupList({ const isMainStatsLoading = isPending(mainStatisticsStatus); const isDetailedStatsLoading = isPending(detailedStatisticsStatus); + const { onPageReady } = usePerformanceContext(); + + useEffect(() => { + // this component is used both for the service overview tab and the errors tab, + // onLoadTable will be defined if it's the service overview tab + if ( + mainStatisticsStatus === FETCH_STATUS.SUCCESS && + detailedStatisticsStatus === FETCH_STATUS.SUCCESS && + !hasTableLoaded.current + ) { + if (onLoadTable) { + onLoadTable(); + } else { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + hasTableLoaded.current = true; + } + }, [ + mainStatisticsStatus, + detailedStatisticsStatus, + onLoadTable, + rangeFrom, + rangeTo, + onPageReady, + ]); const columns = useMemo(() => { const groupIdColumn: ITableColumn = { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/onboarding/instructions/dotnet_agent.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/onboarding/instructions/dotnet_agent.tsx index a0e12c4cbadac..eb8c5fee46b7c 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/onboarding/instructions/dotnet_agent.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/onboarding/instructions/dotnet_agent.tsx @@ -25,15 +25,15 @@ export const createDotNetAgentInstructions = (commonOptions: AgentInstructions): agentStatus, agentStatusLoading, } = commonOptions; - const codeBlock = `public class Startup -{ - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - app.UseAllElasticApm(Configuration); - //…rest of the method - } - //…rest of the class -}`; + const codeBlock = `var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddAllElasticApm(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +app.Run();`; return [ { title: i18n.translate('xpack.apm.onboarding.dotNet.download.title', { @@ -72,8 +72,8 @@ export const createDotNetAgentInstructions = (commonOptions: AgentInstructions): {i18n.translate('xpack.apm.onboarding.dotNet.configureApplication.textPre', { defaultMessage: - 'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `UseAllElasticApm` \ - method in the `Configure` method within the `Startup.cs` file.', + 'In case of ASP.NET Core with the `Elastic.Apm.NetCoreAll` package, call the `AddAllElasticApm` \ + extension method on the `IServiceCollection` within the `Program.cs` file.', })} @@ -84,7 +84,7 @@ export const createDotNetAgentInstructions = (commonOptions: AgentInstructions): {i18n.translate('xpack.apm.onboarding.dotNet.configureApplication.textPost', { defaultMessage: - 'Passing an `IConfiguration` instance is optional and by doing so, the agent will read config settings through this \ + 'The agent will implicitly read config settings through the application’s \ `IConfiguration` instance (e.g. from the `appsettings.json` file).', })} @@ -119,8 +119,7 @@ export const createDotNetAgentInstructions = (commonOptions: AgentInstructions): {i18n.translate('xpack.apm.onboarding.dotNet.configureAgent.textPost', { defaultMessage: - 'In case you don’t pass an `IConfiguration` instance to the agent (e.g. in case of non ASP.NET Core applications) \ - you can also configure the agent through environment variables. \n \ + 'You can also configure the agent through environment variables. \n \ See [the documentation]({documentationLink}) for advanced usage, including the [Profiler Auto instrumentation]({profilerLink}) quick start.', values: { documentationLink: `${baseUrl}guide/en/apm/agent/dotnet/current/configuration.html`, diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/apm_overview/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/apm_overview/index.tsx index f747319f5c7e8..f35174f39ee15 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/apm_overview/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/apm_overview/index.tsx @@ -7,7 +7,8 @@ import type { EuiFlexGroupProps } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { chartHeight } from '..'; import type { AgentName } from '../../../../../typings/es_schemas/ui/fields/agent'; import { @@ -44,6 +45,24 @@ export function ApmOverview() { } = useApmParams('/services/{serviceName}/overview'); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + const [haveTablesLoaded, setHaveTablesLoaded] = useState({ + transactions: false, + dependencies: false, + errors: false, + }); + const { onPageReady } = usePerformanceContext(); + + useEffect(() => { + const { transactions, dependencies, errors } = haveTablesLoaded; + if (transactions && dependencies && errors) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + }, [haveTablesLoaded, onPageReady, rangeFrom, rangeTo]); const isRumAgent = isRumAgentName(agentName); const isOpenTelemetryAgent = isOpenTelemetryAgentName(agentName as AgentName); @@ -62,6 +81,10 @@ export function ApmOverview() { false ); + const onLoadTable = (key: string) => { + setHaveTablesLoaded((currentValues) => ({ ...currentValues, [key]: true })); + }; + return ( <> {!sloCalloutDismissed && ( @@ -98,6 +121,7 @@ export function ApmOverview() { kuery={kuery} environment={environment} fixedHeight={true} + onLoadTable={() => onLoadTable('transactions')} start={start} end={end} showPerPageOptions={false} @@ -121,7 +145,10 @@ export function ApmOverview() { )} - + onLoadTable('errors')} + /> @@ -151,6 +178,7 @@ export function ApmOverview() { onLoadTable('dependencies')} fixedHeight={true} showPerPageOptions={false} link={ diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview.test.tsx index 71974cfbe75bd..a4eb99db8bfa1 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview.test.tsx @@ -12,6 +12,13 @@ import * as stories from './service_overview.stories'; import * as useAdHocApmDataView from '../../../hooks/use_adhoc_apm_data_view'; import { renderWithTheme } from '../../../utils/test_helpers'; +// Mock the usePerformanceContext hook +jest.mock('@kbn/ebt-tools', () => ({ + usePerformanceContext: () => ({ + onPageReady: jest.fn(), + }), +})); + const { Example } = composeStories(stories); describe('ServiceOverview', () => { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index 34a2be48813ec..3d7889ac0ef49 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -9,8 +9,9 @@ import { EuiIconTip } from '@elastic/eui'; import { METRIC_TYPE } from '@kbn/analytics'; import { i18n } from '@kbn/i18n'; import type { ReactNode } from 'react'; -import React from 'react'; -import { useUiTracker } from '@kbn/observability-shared-plugin/public'; +import React, { useEffect, useRef } from 'react'; +import { FETCH_STATUS, useUiTracker } from '@kbn/observability-shared-plugin/public'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { isTimeComparison } from '../../../shared/time_comparison/get_comparison_options'; import { getNodeName, NodeType } from '../../../../../common/connections'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -26,6 +27,7 @@ interface ServiceOverviewDependenciesTableProps { link?: ReactNode; showPerPageOptions?: boolean; showSparkPlots?: boolean; + onLoadTable?: () => void; } export function ServiceOverviewDependenciesTable({ @@ -33,6 +35,7 @@ export function ServiceOverviewDependenciesTable({ link, showPerPageOptions = true, showSparkPlots, + onLoadTable, }: ServiceOverviewDependenciesTableProps) { const { query: { @@ -50,9 +53,9 @@ export function ServiceOverviewDependenciesTable({ const { start, end } = useTimeRange({ rangeFrom, rangeTo }); const { serviceName, transactionType } = useApmServiceContext(); - + const { onPageReady } = usePerformanceContext(); const trackEvent = useUiTracker(); - + const hasTableLoaded = useRef(false); const { data, status } = useFetcher( (callApmApi) => { if (!start || !end) { @@ -75,6 +78,24 @@ export function ServiceOverviewDependenciesTable({ [start, end, serviceName, environment, offset, comparisonEnabled] ); + useEffect(() => { + // this component is used both for the service overview tab and the transactions tab, + // onLoadTable will be defined if it's the service overview tab + if (status === FETCH_STATUS.SUCCESS && !hasTableLoaded.current) { + if (onLoadTable) { + onLoadTable(); + } else { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + hasTableLoaded.current = true; + } + }, [status, onLoadTable, onPageReady, rangeFrom, rangeTo]); + const dependencies = data?.serviceDependencies.map((dependency) => { const { location } = dependency; diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index a630cb7a2bc60..bbe0c4a901cec 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -15,9 +15,10 @@ import { ErrorGroupList } from '../../error_group_overview/error_group_list'; interface Props { serviceName: string; + onLoadTable?: () => void; } -export function ServiceOverviewErrorsTable({ serviceName }: Props) { +export function ServiceOverviewErrorsTable({ serviceName, onLoadTable }: Props) { const { query } = useApmParams('/services/{serviceName}/overview'); return ( @@ -46,6 +47,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { sample.traceId); }, [traceSamples]); + useEffect(() => { + if (hasLoadedTable) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + customMetrics: { + key1: 'traceIds', + value1: traceIds.length, + }, + }); + } + }, [hasLoadedTable, onPageReady, rangeFrom, rangeTo, traceIds]); + return ( setHasLoadedTable(true)} start={start} end={end} traceIds={traceIds} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/trace_explorer/trace_explorer_waterfall.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/trace_explorer/trace_explorer_waterfall.tsx index e4a9f56de053f..240668d529ebb 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/trace_explorer/trace_explorer_waterfall.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/trace_explorer/trace_explorer_waterfall.tsx @@ -7,6 +7,7 @@ import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public'; import React, { useCallback, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useTimeRange } from '../../../hooks/use_time_range'; import { useTraceExplorerSamples } from '../../../hooks/use_trace_explorer_samples'; @@ -18,7 +19,7 @@ import type { TransactionTab } from '../transaction_details/waterfall_with_summa export function TraceExplorerWaterfall() { const history = useHistory(); - + const { onPageReady } = usePerformanceContext(); const traceSamplesFetchResult = useTraceExplorerSamples(); const { @@ -55,6 +56,21 @@ export function TraceExplorerWaterfall() { end, }); + useEffect(() => { + if (waterfallFetchResult.status === FETCH_STATUS.SUCCESS) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + customMetrics: { + key1: 'traceDocsTotal', + value1: waterfallFetchResult.waterfall.traceDocsTotal, + }, + }); + } + }, [waterfallFetchResult, onPageReady, rangeFrom, rangeTo]); + const onSampleClick = useCallback( (sample: any) => { push(history, { diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx index 5e394643fe157..2a25dba321f62 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_details/transaction_details_tabs.tsx @@ -11,6 +11,7 @@ import type { XYBrushEvent } from '@elastic/charts'; import { EuiPanel, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; import { omit } from 'lodash'; import { useHistory } from 'react-router-dom'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { maybe } from '../../../../common/utils/maybe'; import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; @@ -47,6 +48,7 @@ export function TransactionDetailsTabs() { const isCriticalPathFeatureEnabled = useCriticalPathFeatureEnabledSetting(); const isTransactionProfilingEnabled = useTransactionProfilingSetting(); + const { onPageReady } = usePerformanceContext(); const availableTabs = useMemo(() => { const tabs = [traceSamplesTab, latencyCorrelationsTab, failedTransactionsCorrelationsTab]; @@ -68,7 +70,7 @@ export function TransactionDetailsTabs() { const { component: TabContent } = availableTabs.find((tab) => tab.key === currentTab) ?? traceSamplesTab; - const { environment, kuery, transactionName } = query; + const { environment, kuery, transactionName, rangeFrom, rangeTo } = query; const traceSamplesFetchResult = useTransactionTraceSamplesFetcher({ transactionName, @@ -90,6 +92,21 @@ export function TransactionDetailsTabs() { setCurrentTab(traceSamplesTabKey); }, [traceSamplesTabKey]); + useEffect(() => { + if (traceSamplesFetchResult.status === FETCH_STATUS.SUCCESS) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + customMetrics: { + key1: 'traceDocsTotal', + value1: traceSamplesFetchResult.data?.traceSamples?.length ?? 0, + }, + }); + } + }, [traceSamplesFetchResult, onPageReady, rangeFrom, rangeTo]); + useEffect(() => { const selectedSample = traceSamplesFetchResult.data?.traceSamples.find( (sample) => sample.transactionId === transactionId && sample.traceId === traceId diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/index.tsx index 84da9489964c6..5c2c499a0f7b8 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/index.tsx @@ -6,8 +6,9 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; +import { usePerformanceContext } from '@kbn/ebt-tools'; import { isServerlessAgentName } from '../../../../common/agent_name'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; @@ -36,10 +37,22 @@ export function TransactionOverview() { } = useApmParams('/services/{serviceName}/transactions'); const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - + const [hasLoadedTable, setHasLoadedTable] = useState(false); + const { onPageReady } = usePerformanceContext(); const { transactionType, fallbackToTransactions, serverlessType, serviceName } = useApmServiceContext(); + useEffect(() => { + if (hasLoadedTable) { + onPageReady({ + meta: { + rangeFrom, + rangeTo, + }, + }); + } + }, [hasLoadedTable, onPageReady, rangeFrom, rangeTo]); + const history = useHistory(); // redirect to first transaction type @@ -109,6 +122,7 @@ export function TransactionOverview() { hideViewTransactionsLink numberOfTransactionsPerPage={10} showMaxTransactionGroupsExceededWarning + onLoadTable={() => setHasLoadedTable(true)} environment={environment} kuery={kuery} start={start} diff --git a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx index 947dda02630a5..c77ecf30097a4 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx @@ -25,6 +25,13 @@ import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { ApmTimeRangeMetadataContextProvider } from '../../../context/time_range_metadata/time_range_metadata_context'; import { MockTimeRangeContextProvider } from '../../../context/time_range_metadata/mock_time_range_metadata_context_provider'; +// Mock the usePerformanceContext hook +jest.mock('@kbn/ebt-tools', () => ({ + usePerformanceContext: () => ({ + onPageReady: jest.fn(), + }), +})); + const KibanaReactContext = createKibanaReactContext({ uiSettings: { get: () => true }, usageCollection: { reportUiCounter: () => {} }, diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/critical_path_flamegraph/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/critical_path_flamegraph/index.tsx index 988d2ef547581..ddb25d92fd255 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/critical_path_flamegraph/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/critical_path_flamegraph/index.tsx @@ -10,9 +10,9 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, euiPaletteColorBlind } fr import { css } from '@emotion/css'; import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { uniqueId } from 'lodash'; -import React, { useMemo, useRef } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import type { FETCH_STATUS } from '../../../hooks/use_fetcher'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { useFetcher, isPending } from '../../../hooks/use_fetcher'; import { CriticalPathFlamegraphTooltip } from './critical_path_flamegraph_tooltip'; import { criticalPathToFlamegraph } from './critical_path_to_flamegraph'; @@ -27,9 +27,10 @@ export function CriticalPathFlamegraph( end: string; traceIds: string[]; traceIdsFetchStatus: FETCH_STATUS; + onLoadTable?: () => void; } & ({ serviceName: string; transactionName: string } | {}) ) { - const { start, end, traceIds, traceIdsFetchStatus } = props; + const { start, end, traceIds, traceIdsFetchStatus, onLoadTable } = props; const serviceName = 'serviceName' in props ? props.serviceName : null; const transactionName = 'transactionName' in props ? props.transactionName : null; @@ -40,6 +41,7 @@ export function CriticalPathFlamegraph( // of the search. const timerange = useRef({ start, end }); timerange.current = { start, end }; + const [hasTableLoaded, setHasTableLoaded] = useState(false); const { data: { criticalPath } = { criticalPath: null }, status: criticalPathFetchStatus } = useFetcher( @@ -63,6 +65,24 @@ export function CriticalPathFlamegraph( [timerange, traceIds, serviceName, transactionName] ); + useEffect(() => { + if ( + criticalPathFetchStatus === FETCH_STATUS.SUCCESS && + traceIdsFetchStatus === FETCH_STATUS.SUCCESS && + onLoadTable && + !hasTableLoaded + ) { + onLoadTable(); + setHasTableLoaded(true); + } + }, [ + criticalPathFetchStatus, + onLoadTable, + hasTableLoaded, + traceIdsFetchStatus, + setHasTableLoaded, + ]); + const chartThemes = useChartThemes(); const isLoading = isPending(traceIdsFetchStatus) || isPending(criticalPathFetchStatus); diff --git a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx index 4db1ab4d4a0e3..75703b3298332 100644 --- a/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx +++ b/x-pack/solutions/observability/plugins/apm/public/components/shared/transactions_table/index.tsx @@ -55,6 +55,7 @@ interface Props { end: string; saveTableOptionsToUrl?: boolean; showSparkPlots?: boolean; + onLoadTable?: () => void; } export function TransactionsTable({ @@ -69,6 +70,7 @@ export function TransactionsTable({ start, end, saveTableOptionsToUrl = false, + onLoadTable, showSparkPlots, }: Props) { const { link } = useApmRouter(); @@ -89,7 +91,7 @@ export function TransactionsTable({ const shouldShowSparkPlots = showSparkPlots ?? !isLarge; const { transactionType, serviceName } = useApmServiceContext(); const [searchQuery, setSearchQueryDebounced] = useStateDebounced(''); - + const [hasTableLoaded, setHasTableLoaded] = useState(false); const [renderedItems, setRenderedItems] = useState([]); const { mainStatistics, mainStatisticsStatus, detailedStatistics, detailedStatisticsStatus } = @@ -107,6 +109,18 @@ export function TransactionsTable({ transactionType, }); + useEffect(() => { + if ( + mainStatisticsStatus === FETCH_STATUS.SUCCESS && + detailedStatisticsStatus === FETCH_STATUS.SUCCESS && + onLoadTable && + !hasTableLoaded + ) { + onLoadTable(); + setHasTableLoaded(true); + } + }, [mainStatisticsStatus, detailedStatisticsStatus, onLoadTable, hasTableLoaded]); + const columns = useMemo(() => { return getColumns({ serviceName, diff --git a/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts b/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts index aa7fcd8868f17..e61fa75dfa4c1 100644 --- a/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts +++ b/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_group_main_statistics.ts @@ -176,7 +176,7 @@ export async function getErrorGroupMainStatistics({ error: { ...(event.error ?? {}), exception: - (errorSource?.error.exception?.length ?? 0) > 1 + (errorSource?.error.exception?.length ?? 0) > 0 ? errorSource?.error.exception : event?.error.exception && [event.error.exception], }, diff --git a/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts b/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts index 4217ec526c564..c16896f2a80c1 100644 --- a/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts +++ b/x-pack/solutions/observability/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts @@ -159,7 +159,7 @@ export async function getErrorSampleDetails({ error: { ...errorFromFields.error, exception: - (source?.error.exception?.length ?? 0) > 1 + (source?.error.exception?.length ?? 0) > 0 ? source?.error.exception : errorFromFields?.error.exception && [errorFromFields.error.exception], log: source?.error?.log, diff --git a/x-pack/solutions/observability/plugins/apm/server/routes/mobile/crashes/get_crash_groups/get_crash_group_main_statistics.ts b/x-pack/solutions/observability/plugins/apm/server/routes/mobile/crashes/get_crash_groups/get_crash_group_main_statistics.ts index 51cecac6a6989..ceb5a4e389617 100644 --- a/x-pack/solutions/observability/plugins/apm/server/routes/mobile/crashes/get_crash_groups/get_crash_group_main_statistics.ts +++ b/x-pack/solutions/observability/plugins/apm/server/routes/mobile/crashes/get_crash_groups/get_crash_group_main_statistics.ts @@ -142,7 +142,7 @@ export async function getMobileCrashGroupMainStatistics({ error: { ...(event.error ?? {}), exception: - (errorSource?.error.exception?.length ?? 0) > 1 + (errorSource?.error.exception?.length ?? 0) > 0 ? errorSource?.error.exception : event?.error.exception && [event.error.exception], }, diff --git a/x-pack/solutions/observability/plugins/apm/server/routes/mobile/errors/get_mobile_error_group_main_statistics.ts b/x-pack/solutions/observability/plugins/apm/server/routes/mobile/errors/get_mobile_error_group_main_statistics.ts index f27a487c8b8bc..afeea78a43214 100644 --- a/x-pack/solutions/observability/plugins/apm/server/routes/mobile/errors/get_mobile_error_group_main_statistics.ts +++ b/x-pack/solutions/observability/plugins/apm/server/routes/mobile/errors/get_mobile_error_group_main_statistics.ts @@ -143,7 +143,7 @@ export async function getMobileErrorGroupMainStatistics({ error: { ...(event.error ?? {}), exception: - (errorSource?.error.exception?.length ?? 0) > 1 + (errorSource?.error.exception?.length ?? 0) > 0 ? errorSource?.error.exception : event?.error.exception && [event.error.exception], }, diff --git a/x-pack/solutions/observability/plugins/apm/server/routes/traces/get_trace_items.ts b/x-pack/solutions/observability/plugins/apm/server/routes/traces/get_trace_items.ts index 23df885e65266..4f6222f9fb8a6 100644 --- a/x-pack/solutions/observability/plugins/apm/server/routes/traces/get_trace_items.ts +++ b/x-pack/solutions/observability/plugins/apm/server/routes/traces/get_trace_items.ts @@ -166,7 +166,7 @@ export async function getTraceItems({ error: { ...(event.error ?? {}), exception: - (errorSource?.error.exception?.length ?? 0) > 1 + (errorSource?.error.exception?.length ?? 0) > 0 ? errorSource?.error.exception : event?.error.exception && [event.error.exception], log: errorSource?.error.log, diff --git a/x-pack/solutions/observability/plugins/infra/common/constants.ts b/x-pack/solutions/observability/plugins/infra/common/constants.ts index 24e77e870fac3..f1d4b8ce4a297 100644 --- a/x-pack/solutions/observability/plugins/infra/common/constants.ts +++ b/x-pack/solutions/observability/plugins/infra/common/constants.ts @@ -35,7 +35,7 @@ export const TIEBREAKER_FIELD = '_doc'; export const HOST_NAME_FIELD = 'host.name'; export const CONTAINER_ID_FIELD = 'container.id'; export const KUBERNETES_POD_UID_FIELD = 'kubernetes.pod.uid'; -export const SYSTEM_PROCESS_CMDLINE_FIELD = 'system.process.cmdline'; +export const PROCESS_COMMANDLINE_FIELD = 'process.command_line'; export const EVENT_MODULE = 'event.module'; export const METRICSET_MODULE = 'metricset.module'; export const METRICSET_NAME = 'metricset.name'; diff --git a/x-pack/solutions/observability/plugins/infra/common/http_api/host_details/process_list.ts b/x-pack/solutions/observability/plugins/infra/common/http_api/host_details/process_list.ts index e595049bb7294..1fe1ed9e86b26 100644 --- a/x-pack/solutions/observability/plugins/infra/common/http_api/host_details/process_list.ts +++ b/x-pack/solutions/observability/plugins/infra/common/http_api/host_details/process_list.ts @@ -58,6 +58,7 @@ export const ProcessListAPIQueryAggregationRT = rt.type({ _source: rt.type({ process: rt.type({ pid: rt.number, + command_line: rt.string, }), system: rt.type({ process: rt.type({ diff --git a/x-pack/solutions/observability/plugins/infra/public/alerting/common/components/threshold.tsx b/x-pack/solutions/observability/plugins/infra/public/alerting/common/components/threshold.tsx index 91b1d249cfbf3..cfc508203a045 100644 --- a/x-pack/solutions/observability/plugins/infra/public/alerting/common/components/threshold.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/alerting/common/components/threshold.tsx @@ -31,6 +31,14 @@ export interface Props { }; } +const NO_DATA_VALUE = i18n.translate('xpack.infra.alerting.noDataValue', { + defaultMessage: 'No Data', +}); + +const THRESHOLD_NO_DATA_TITLE = i18n.translate('xpack.infra.alerting.thresholdNoDataTitle', { + defaultMessage: 'Alert when', +}); + export const Threshold = ({ chartProps: { theme, baseTheme }, comparator, @@ -64,7 +72,7 @@ export const Threshold = ({ [ { title, - extra: ( + extra: value ? ( <> {i18n.translate('xpack.infra.alerting.thresholdExtraTitle', { values: { @@ -83,9 +91,11 @@ export const Threshold = ({ defaultMessage: `Warn when {comparator} {threshold}`, })} + ) : ( + <>{THRESHOLD_NO_DATA_TITLE} ), color, - value, + value: value ?? NO_DATA_VALUE, valueFormatter, icon: ({ width, height, color: iconColor }) => ( diff --git a/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/parse_search_string.ts b/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/parse_search_string.ts index 7112dbed917a6..bd8924266114c 100644 --- a/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/parse_search_string.ts +++ b/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/parse_search_string.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { PROCESS_COMMANDLINE_FIELD } from '../../../../../common/constants'; + export const parseSearchString = (query: string) => { if (query.trim() === '') { return [ @@ -22,7 +24,7 @@ export const parseSearchString = (query: string) => { return [ ...cmdlineFilters.map((clause) => ({ query_string: { - fields: ['system.process.cmdline'], + fields: [PROCESS_COMMANDLINE_FIELD], query: `*${escapeReservedCharacters(clause)}*`, minimum_should_match: 1, }, diff --git a/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/process_row_charts.tsx b/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/process_row_charts.tsx index 80bbd66077321..44e957c92c212 100644 --- a/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/process_row_charts.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/components/asset_details/tabs/processes/process_row_charts.tsx @@ -166,44 +166,40 @@ const ProcessChart = ({ timeseries, color, label }: ProcessChartProps) => { : { max: 0, min: 0 }; return ( -
- - - - - moment(value).format('Y-MM-DD HH:mm:ss.SSS')} /> - - -
+ + + + moment(value).format('Y-MM-DD HH:mm:ss.SSS')} /> + + ); }; diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/solutions/observability/plugins/infra/public/pages/logs/page_content.tsx index 774eef04e6274..c8e024dbe77e3 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/pages/logs/page_content.tsx @@ -16,6 +16,7 @@ import { type ObservabilityOnboardingLocatorParams, OBSERVABILITY_ONBOARDING_LOCATOR, } from '@kbn/deeplinks-observability'; +import { MANAGEMENT_APP_LOCATOR } from '@kbn/deeplinks-management/constants'; import { dynamic } from '@kbn/shared-ux-utility'; import { type LogsLocatorParams, LOGS_LOCATOR_ID } from '@kbn/logs-shared-plugin/common'; import { LazyAlertDropdownWrapper } from '../../alerting/log_threshold'; @@ -40,6 +41,9 @@ export const LogsPageContent: React.FunctionComponent = () => { const onboardingLocator = share?.url.locators.get( OBSERVABILITY_ONBOARDING_LOCATOR ); + + const managementLocator = share?.url.locators.get(MANAGEMENT_APP_LOCATOR); + const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext); useReadOnlyBadge(!uiCapabilities?.logs?.save); @@ -84,7 +88,19 @@ export const LogsPageContent: React.FunctionComponent = () => { + // Legacy renders and redirects + { + managementLocator?.navigate({ + sectionId: 'kibana', + appId: 'settings?query=observability%3AlogSources', + }); + return null; + }} + /> } /> diff --git a/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list.ts b/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list.ts index aa911a074edcd..89643df938089 100644 --- a/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list.ts +++ b/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { TIMESTAMP_FIELD, SYSTEM_PROCESS_CMDLINE_FIELD } from '../../../common/constants'; +import { TIMESTAMP_FIELD, PROCESS_COMMANDLINE_FIELD } from '../../../common/constants'; import type { ProcessListAPIRequest, ProcessListAPIQueryAggregation, @@ -72,7 +72,7 @@ export const getProcessList = async ( aggs: { filteredProcs: { terms: { - field: SYSTEM_PROCESS_CMDLINE_FIELD, + field: PROCESS_COMMANDLINE_FIELD, size: TOP_N, order: { [sortBy.name]: sortBy.isAscending ? 'asc' : 'desc', @@ -104,7 +104,12 @@ export const getProcessList = async ( }, }, ], - _source: ['system.process.state', 'user.name', 'process.pid'], + _source: [ + 'system.process.state', + 'user.name', + 'process.pid', + 'process.command_line', + ], }, }, }, diff --git a/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list_chart.ts b/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list_chart.ts index e0cc6d2e80cdd..5f832224ec204 100644 --- a/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list_chart.ts +++ b/x-pack/solutions/observability/plugins/infra/server/lib/host_details/process_list_chart.ts @@ -6,7 +6,7 @@ */ import { first } from 'lodash'; -import { TIMESTAMP_FIELD, SYSTEM_PROCESS_CMDLINE_FIELD } from '../../../common/constants'; +import { TIMESTAMP_FIELD, PROCESS_COMMANDLINE_FIELD } from '../../../common/constants'; import type { ProcessListAPIChartRequest, ProcessListAPIChartQueryAggregation, @@ -48,7 +48,7 @@ export const getProcessListChart = async ( must: [ { match: { - [SYSTEM_PROCESS_CMDLINE_FIELD]: command, + [PROCESS_COMMANDLINE_FIELD]: command, }, }, ], @@ -57,7 +57,7 @@ export const getProcessListChart = async ( aggs: { filteredProc: { terms: { - field: SYSTEM_PROCESS_CMDLINE_FIELD, + field: PROCESS_COMMANDLINE_FIELD, size: 1, }, aggs: { diff --git a/x-pack/solutions/observability/plugins/infra/tsconfig.json b/x-pack/solutions/observability/plugins/infra/tsconfig.json index 78b37f62b9f40..0177b6129f96b 100644 --- a/x-pack/solutions/observability/plugins/infra/tsconfig.json +++ b/x-pack/solutions/observability/plugins/infra/tsconfig.json @@ -116,7 +116,8 @@ "@kbn/observability-utils-common", "@kbn/charts-theme", "@kbn/response-ops-rule-params", - "@kbn/core-test-helpers-model-versions" + "@kbn/core-test-helpers-model-versions", + "@kbn/deeplinks-management" ], "exclude": ["target/**/*"] } diff --git a/x-pack/solutions/observability/plugins/observability/public/components/custom_threshold/components/threshold.tsx b/x-pack/solutions/observability/plugins/observability/public/components/custom_threshold/components/threshold.tsx index 347730fe4dea6..593fce65e2102 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/custom_threshold/components/threshold.tsx +++ b/x-pack/solutions/observability/plugins/observability/public/components/custom_threshold/components/threshold.tsx @@ -27,6 +27,17 @@ export interface Props { valueFormatter?: ValueFormatter; } +const NO_DATA_VALUE = i18n.translate('xpack.observability.customThreshold.rule.noDataValue', { + defaultMessage: 'No Data', +}); + +const THRESHOLD_NO_DATA_TITLE = i18n.translate( + 'xpack.observability.customThreshold.rule.thresholdNoDataTitle', + { + defaultMessage: 'Alert when', + } +); + export function Threshold({ chartProps: { theme, baseTheme }, comparator, @@ -61,20 +72,22 @@ export function Threshold({ title, extra: ( - {i18n.translate( - 'xpack.observability.customThreshold.rule.thresholdExtraTitle', - { - values: { - comparator, - threshold: threshold.map((t) => valueFormatter(t)).join(' - '), - }, - defaultMessage: `Alert when {comparator} {threshold}`, - } - )} + {value + ? i18n.translate( + 'xpack.observability.customThreshold.rule.thresholdExtraTitle', + { + values: { + comparator, + threshold: threshold.map((t) => valueFormatter(t)).join(' - '), + }, + defaultMessage: `Alert when {comparator} {threshold}`, + } + ) + : THRESHOLD_NO_DATA_TITLE} ), color, - value, + value: value ?? NO_DATA_VALUE, valueFormatter, icon: ({ width, height, color: iconColor }) => ( diff --git a/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.test.ts b/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.test.ts index 0aedbb5723219..04becb43a7b30 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.test.ts +++ b/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.test.ts @@ -19,8 +19,8 @@ const useCases = [ }, { operation: 'sum', - operationWithField: 'sum(system.cpu.user.pct)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'sum("system.cpu.user.pct")', + sourceField: '"system.cpu.user.pct"', }, ], [ @@ -32,8 +32,8 @@ const useCases = [ }, { operation: 'max', - operationWithField: 'max(system.cpu.user.pct)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'max("system.cpu.user.pct")', + sourceField: '"system.cpu.user.pct"', }, ], [ @@ -45,8 +45,8 @@ const useCases = [ }, { operation: 'min', - operationWithField: 'min(system.cpu.user.pct)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'min("system.cpu.user.pct")', + sourceField: '"system.cpu.user.pct"', }, ], [ @@ -58,8 +58,8 @@ const useCases = [ }, { operation: 'average', - operationWithField: 'average(system.cpu.user.pct)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'average("system.cpu.user.pct")', + sourceField: '"system.cpu.user.pct"', }, ], [ @@ -72,7 +72,7 @@ const useCases = [ { operation: 'count', operationWithField: `count(kql='system.cpu.user.pct: *')`, - sourceField: '', + sourceField: '""', }, ], [ @@ -85,7 +85,7 @@ const useCases = [ { operation: 'count', operationWithField: `count(kql='container.name:container\\'s name-1')`, - sourceField: '', + sourceField: '""', }, ], [ @@ -98,7 +98,7 @@ const useCases = [ { operation: 'count', operationWithField: `count(kql='host.name: host-*')`, - sourceField: '', + sourceField: '""', }, ], [ @@ -110,8 +110,21 @@ const useCases = [ }, { operation: 'unique_count', - operationWithField: 'unique_count(system.cpu.user.pct)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'unique_count("system.cpu.user.pct")', + sourceField: '"system.cpu.user.pct"', + }, + ], + [ + { + aggType: Aggregators.CARDINALITY, + field: 'field.name/with/slashes', + filter: '', + name: '', + }, + { + operation: 'unique_count', + operationWithField: 'unique_count("field.name/with/slashes")', + sourceField: '"field.name/with/slashes"', }, ], [ @@ -123,8 +136,8 @@ const useCases = [ }, { operation: 'percentile', - operationWithField: 'percentile(system.cpu.user.pct, percentile=95)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'percentile("system.cpu.user.pct", percentile=95)', + sourceField: '"system.cpu.user.pct"', }, ], [ @@ -136,8 +149,8 @@ const useCases = [ }, { operation: 'percentile', - operationWithField: 'percentile(system.cpu.user.pct, percentile=99)', - sourceField: 'system.cpu.user.pct', + operationWithField: 'percentile("system.cpu.user.pct", percentile=99)', + sourceField: '"system.cpu.user.pct"', }, ], [ @@ -149,8 +162,8 @@ const useCases = [ }, { operation: 'counter_rate', - operationWithField: `counter_rate(max(system.network.in.bytes), kql='')`, - sourceField: 'system.network.in.bytes', + operationWithField: `counter_rate(max("system.network.in.bytes"), kql='')`, + sourceField: '"system.network.in.bytes"', }, ], [ @@ -162,8 +175,8 @@ const useCases = [ }, { operation: 'counter_rate', - operationWithField: `counter_rate(max(system.network.in.bytes), kql='host.name : "foo"')`, - sourceField: 'system.network.in.bytes', + operationWithField: `counter_rate(max("system.network.in.bytes"), kql='host.name : "foo"')`, + sourceField: '"system.network.in.bytes"', }, ], ]; diff --git a/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.ts b/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.ts index 775da2fc1c805..61f65fd65c8ac 100644 --- a/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.ts +++ b/x-pack/solutions/observability/plugins/observability/public/components/rule_condition_chart/helpers.ts @@ -23,8 +23,8 @@ export const getLensOperationFromRuleMetric = (metric: GenericMetric): LensOpera if (aggType === Aggregators.RATE) { return { operation: 'counter_rate', - operationWithField: `counter_rate(max(${field}), kql='${escapedFilter}')`, - sourceField: field || '', + operationWithField: `counter_rate(max("${field}"), kql='${escapedFilter}')`, + sourceField: `"${field}"` || '', }; } @@ -34,7 +34,7 @@ export const getLensOperationFromRuleMetric = (metric: GenericMetric): LensOpera if (aggType === Aggregators.COUNT) operation = 'count'; if (field) { - operationArgs.push(field); + operationArgs.push(`"${field}"`); } if (aggType === Aggregators.P95) { @@ -49,7 +49,7 @@ export const getLensOperationFromRuleMetric = (metric: GenericMetric): LensOpera return { operation, operationWithField: `${operation}(${operationArgs.join(', ')})`, - sourceField: field || '', + sourceField: `"${field}"` || '', }; }; diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts index fb5aef4e3ddcb..804ae09e5ee9e 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts @@ -155,6 +155,7 @@ const mockedIndex = { const mockedDataView = { getIndexPattern: () => 'mockedIndexPattern', getName: () => 'mockedDataViewName', + getRuntimeMappings: () => undefined, ...mockedIndex, }; const mockedSearchSource = { @@ -971,7 +972,7 @@ describe('The custom threshold alert type', () => { stateResult2 ); expect(stateResult3.missingGroups).toEqual([{ key: 'b', bucketKey: { groupBy0: 'b' } }]); - expect(mockedEvaluateRule.mock.calls[2][10]).toEqual([ + expect(mockedEvaluateRule.mock.calls[2][11]).toEqual([ { bucketKey: { groupBy0: 'b' }, key: 'b' }, ]); }); @@ -2961,7 +2962,7 @@ describe('The custom threshold alert type', () => { stateResult2 ); expect(stateResult3.missingGroups).toEqual([{ key: 'b', bucketKey: { groupBy0: 'b' } }]); - expect(mockedEvaluateRule.mock.calls[2][10]).toEqual([ + expect(mockedEvaluateRule.mock.calls[2][11]).toEqual([ { bucketKey: { groupBy0: 'b' }, key: 'b' }, ]); }); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts index 9e3b16dc6c48c..b298e4ab59972 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts @@ -126,6 +126,7 @@ export const createCustomThresholdExecutor = ({ const initialSearchSource = await searchSourceClient.create(params.searchConfiguration); const dataView = initialSearchSource.getField('index')!; const { id: dataViewId, timeFieldName } = dataView; + const runtimeMappings = dataView.getRuntimeMappings(); const dataViewIndexPattern = dataView.getIndexPattern(); const dataViewName = dataView.getName(); if (!dataViewIndexPattern) { @@ -147,6 +148,7 @@ export const createCustomThresholdExecutor = ({ logger, { end: dateEnd, start: dateStart }, esQueryConfig, + runtimeMappings, state.lastRunTimestamp, previousMissingGroups ); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts index 8c5b75f00003a..2d8952a98fa78 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/check_missing_group.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core/server'; import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { EsQueryConfig } from '@kbn/es-query'; @@ -16,6 +17,7 @@ import { } from '../../../../../common/custom_threshold_rule/types'; import type { BucketKey } from './get_data'; import { calculateCurrentTimeFrame, createBoolQuery } from './metric_query'; +import { isPopulatedObject } from './is_populated_object'; export interface MissingGroupsRecord { key: string; @@ -32,7 +34,8 @@ export const checkMissingGroups = async ( logger: Logger, timeframe: { start: number; end: number }, esQueryConfig: EsQueryConfig, - missingGroups: MissingGroupsRecord[] = [] + missingGroups: MissingGroupsRecord[] = [], + runtimeMappings?: estypes.MappingRuntimeFields ): Promise => { if (missingGroups.length === 0) { return missingGroups; @@ -65,6 +68,7 @@ export const checkMissingGroups = async ( terminate_after: 1, track_total_hits: true, query, + ...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}), }, ]; }); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts index 2e2d1e5af48b2..e0d861f53ae38 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/evaluate_rule.ts @@ -6,6 +6,7 @@ */ import moment from 'moment'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core/server'; import { EsQueryConfig } from '@kbn/es-query'; import type { Logger } from '@kbn/logging'; @@ -45,6 +46,7 @@ export const evaluateRule = async >> => { @@ -77,6 +79,7 @@ export const evaluateRule = async @@ -170,6 +172,7 @@ export const getData = async ( alertOnGroupDisappear, timeframe, logger, + runtimeMappings, lastPeriodEnd, previous, nextAfterKey @@ -209,6 +212,7 @@ export const getData = async ( alertOnGroupDisappear, searchConfiguration, esQueryConfig, + runtimeMappings, lastPeriodEnd, groupBy, afterKey, diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/is_populated_object.test.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/is_populated_object.test.ts new file mode 100644 index 0000000000000..fdbe8d9fa5f21 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/is_populated_object.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isPopulatedObject } from './is_populated_object'; + +describe('isPopulatedObject', () => { + it('does not allow numbers', () => { + expect(isPopulatedObject(0)).toBe(false); + }); + it('does not allow strings', () => { + expect(isPopulatedObject('')).toBe(false); + }); + it('does not allow null', () => { + expect(isPopulatedObject(null)).toBe(false); + }); + it('does not allow an empty object', () => { + expect(isPopulatedObject({})).toBe(false); + }); + it('allows an object with an attribute', () => { + expect(isPopulatedObject({ attribute: 'value' })).toBe(true); + }); + it('does not allow an object with a non-existing required attribute', () => { + expect(isPopulatedObject({ attribute: 'value' }, ['otherAttribute'])).toBe(false); + }); + it('allows an object with an existing required attribute', () => { + expect(isPopulatedObject({ attribute: 'value' }, ['attribute'])).toBe(true); + }); + it('allows an object with two existing required attributes', () => { + expect( + isPopulatedObject({ attribute1: 'value1', attribute2: 'value2' }, [ + 'attribute1', + 'attribute2', + ]) + ).toBe(true); + }); + it('does not allow an object with two required attributes where one does not exist', () => { + expect( + isPopulatedObject({ attribute1: 'value1', attribute2: 'value2' }, [ + 'attribute1', + 'otherAttribute', + ]) + ).toBe(false); + }); + it('does not allow an object with a required attribute in the prototype ', () => { + const testObject = { attribute: 'value', __proto__: { otherAttribute: 'value' } }; + expect(isPopulatedObject(testObject, ['otherAttribute'])).toBe(false); + }); +}); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/is_populated_object.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/is_populated_object.ts new file mode 100644 index 0000000000000..7fee714dbd6a0 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/is_populated_object.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** + * A type guard to check record like object structures. + * + * Examples: + * - `isPopulatedObject({...})` + * Limits type to Record + * + * - `isPopulatedObject({...}, ['attribute'])` + * Limits type to Record<'attribute', unknown> + * + * - `isPopulatedObject({...})` + * Limits type to a record with keys of the given interface. + * Note that you might want to add keys from the interface to the + * array of requiredAttributes to satisfy runtime requirements. + * Otherwise you'd just satisfy TS requirements but might still + * run into runtime issues. + */ +export const isPopulatedObject = ( + arg: unknown, + requiredAttributes: U[] = [] +): arg is Record => { + return ( + typeof arg === 'object' && + arg !== null && + Object.keys(arg).length > 0 && + (requiredAttributes.length === 0 || requiredAttributes.every((d) => Object.hasOwn(arg, d))) + ); +}; diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts index 53955b7130c54..a6981b4c02f38 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts @@ -64,6 +64,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { true, searchConfiguration, esQueryConfig, + undefined, void 0, groupBy ); @@ -121,6 +122,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { true, currentSearchConfiguration, esQueryConfig, + undefined, void 0, groupBy ); @@ -233,6 +235,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { true, currentSearchConfiguration, esQueryConfig, + undefined, void 0, groupBy ); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts index 4e7fc236bfdf5..8cf6867aa4a01 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/lib/metric_query.ts @@ -6,6 +6,7 @@ */ import moment from 'moment'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { EsQueryConfig, Filter } from '@kbn/es-query'; import { @@ -24,6 +25,7 @@ import { } from '../utils'; import { createBucketSelector } from './create_bucket_selector'; import { wrapInCurrentPeriod } from './wrap_in_period'; +import { isPopulatedObject } from './is_populated_object'; export const calculateCurrentTimeFrame = ( metricParams: CustomMetricExpressionParams, @@ -76,6 +78,7 @@ export const getElasticsearchMetricQuery = ( alertOnGroupDisappear: boolean, searchConfiguration: SearchConfigurationType, esQueryConfig: EsQueryConfig, + runtimeMappings?: estypes.MappingRuntimeFields, lastPeriodEnd?: number, groupBy?: string | string[], afterKey?: Record, @@ -211,6 +214,7 @@ export const getElasticsearchMetricQuery = ( return { track_total_hits: true, query, + ...(isPopulatedObject(runtimeMappings) ? { runtime_mappings: runtimeMappings } : {}), size: 0, aggs, }; diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts index 2fe8cbe76d294..40b32835a0656 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts @@ -6,22 +6,17 @@ */ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; -import { schema } from '@kbn/config-schema'; import { extractReferences, injectReferences } from '@kbn/data-plugin/common'; -import { dataViewSpecSchema } from '@kbn/data-views-plugin/server/rest_api_routes/schema'; import { i18n } from '@kbn/i18n'; import { IRuleTypeAlerts, GetViewInAppRelativeUrlFnOpts } from '@kbn/alerting-plugin/server'; import { IBasePath, Logger } from '@kbn/core/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { LicenseType } from '@kbn/licensing-plugin/server'; -import { COMPARATORS } from '@kbn/alerting-comparators'; import { EsQueryRuleParamsExtractedParams } from '@kbn/stack-alerts-plugin/server/rule_types/es_query/rule_type_params'; -import { LEGACY_COMPARATORS } from '../../../../common/utils/convert_legacy_outside_comparator'; +import { customThresholdParamsSchema } from '@kbn/response-ops-rule-params/custom_threshold'; import { observabilityFeatureId, observabilityPaths } from '../../../../common'; -import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { THRESHOLD_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; - import { alertDetailUrlActionVariableDescription, cloudActionVariableDescription, @@ -36,7 +31,6 @@ import { valueActionVariableDescription, viewInAppUrlActionVariableDescription, } from './translations'; -import { oneOfLiterals, validateKQLStringFilter } from './utils'; import { createCustomThresholdExecutor, CustomThresholdLocators, @@ -53,79 +47,12 @@ export const MetricsRulesTypeAlertDefinition: IRuleTypeAlerts { it('flattens multi level item', () => { @@ -57,29 +57,6 @@ describe('FlattenObject', () => { }); }); -describe('validateKQLStringFilter', () => { - const data = [ - // input, output - ['', undefined], - ['host.name:host-0', undefined], - ]; - const dataWithError = [ - // input, output - [ - ':*', - 'filterQuery must be a valid KQL filter (error: Expected "(", NOT, end of input, field name, value, whitespace but ":" found.', - ], - ]; - - test.each(data)('validateKQLStringFilter(%s): %o', (input: any, output: any) => { - expect(validateKQLStringFilter(input)).toEqual(output); - }); - - test.each(dataWithError)('validateKQLStringFilter(%s): %o', (input: any, output: any) => { - expect(validateKQLStringFilter(input)).toContain(output); - }); -}); - describe('getFormattedGroupBy', () => { it('should format groupBy correctly for empty input', () => { expect(getFormattedGroupBy(undefined, new Set())).toEqual({}); diff --git a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts index ca9b378d74eaa..fd8420858a430 100644 --- a/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts +++ b/x-pack/solutions/observability/plugins/observability/server/lib/rules/custom_threshold/utils.ts @@ -6,8 +6,6 @@ */ import { isError } from 'lodash'; -import { buildEsQuery as kbnBuildEsQuery } from '@kbn/es-query'; -import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { Logger, LogMeta } from '@kbn/logging'; import type { ElasticsearchClient, IBasePath } from '@kbn/core/server'; @@ -45,26 +43,6 @@ export const oneOfLiterals = (arrayOfLiterals: Readonly) => arrayOfLiterals.includes(value) ? undefined : `must be one of ${arrayOfLiterals.join(' | ')}`, }); -export const validateKQLStringFilter = (value: string) => { - if (value === '') { - // Allow clearing the filter. - return; - } - - try { - kbnBuildEsQuery(undefined, [{ query: value, language: 'kuery' }], [], { - allowLeadingWildcards: true, - queryStringOptions: {}, - ignoreFilterIfFieldNotInIndex: false, - }); - } catch (e) { - return i18n.translate('xpack.observability.customThreshold.rule.schema.invalidFilterQuery', { - defaultMessage: 'filterQuery must be a valid KQL filter (error: {errorMessage})', - values: { errorMessage: e?.message }, - }); - } -}; - export const createScopedLogger = ( logger: Logger, scope: string, diff --git a/x-pack/solutions/observability/plugins/observability/tsconfig.json b/x-pack/solutions/observability/plugins/observability/tsconfig.json index d78e468013be7..b096a09f5eeb2 100644 --- a/x-pack/solutions/observability/plugins/observability/tsconfig.json +++ b/x-pack/solutions/observability/plugins/observability/tsconfig.json @@ -114,7 +114,8 @@ "@kbn/response-ops-rule-form", "@kbn/streams-plugin", "@kbn/data-service", - "@kbn/ebt-tools" + "@kbn/ebt-tools", + "@kbn/response-ops-rule-params" ], "exclude": ["target/**/*"] } diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/common/constants.ts b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/common/constants.ts new file mode 100644 index 0000000000000..f5977cd72ad7b --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/common/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. + */ + +import { + ALERT_STATUS_ACTIVE, + ALERT_STATUS_RECOVERED, + ALERT_STATUS_UNTRACKED, +} from '@kbn/rule-data-utils'; + +export const ALERT_STATUSES = [ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, ALERT_STATUS_UNTRACKED]; diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/ai_assistant.tsx b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/ai_assistant.tsx index 79d9678733941..ea3590740ab57 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/ai_assistant.tsx +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/ai_assistant.tsx @@ -36,7 +36,11 @@ export function getConnectorType( actionParams: ObsAIAssistantActionParams ): Promise> => { const validationResult = { - errors: { connector: new Array(), message: new Array() }, + errors: { + connector: [] as string[], + message: [] as string[], + prompts: [] as string[], + }, }; if (!actionParams.connector) { diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/types.ts b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/types.ts index ed19f3cb74794..08afd5b62a14b 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/types.ts +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/rule_connector/types.ts @@ -7,5 +7,9 @@ export interface ObsAIAssistantActionParams { connector: string; - message: string; + prompts?: Array<{ + message: string; + statuses: string[]; + }>; + message?: string; // this is a legacy field } diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.test.ts b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.test.ts index 04fd10c3e506f..e2c0f97d14a0d 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.test.ts +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.test.ts @@ -8,7 +8,9 @@ import { AlertHit } from '@kbn/alerting-plugin/server/types'; import { ObservabilityAIAssistantRouteHandlerResources } from '@kbn/observability-ai-assistant-plugin/server/routes/types'; import { getFakeKibanaRequest } from '@kbn/security-plugin/server/authentication/api_keys/fake_kibana_request'; +import { ALERT_STATUS_ACTIVE } from '@kbn/rule-data-utils'; import { OBSERVABILITY_AI_ASSISTANT_CONNECTOR_ID } from '../../common/rule_connector'; +import { ALERT_STATUSES } from '../../common/constants'; import { getObsAIAssistantConnectorAdapter, getObsAIAssistantConnectorType, @@ -18,6 +20,63 @@ import { Observable } from 'rxjs'; import { MessageRole } from '@kbn/observability-ai-assistant-plugin/public'; import { AlertDetailsContextualInsightsService } from '@kbn/observability-plugin/server/services'; +const buildConversation = (contentMessage: string) => [ + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.System, + content: '', + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.User, + content: contentMessage, + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.Assistant, + content: '', + function_call: { + name: 'get_alerts_context', + arguments: JSON.stringify({}), + trigger: MessageRole.Assistant as const, + }, + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.User, + name: 'get_alerts_context', + content: expect.any(String), + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.Assistant, + content: '', + function_call: { + name: 'get_connectors', + arguments: JSON.stringify({}), + trigger: MessageRole.Assistant as const, + }, + }, + }, + { + '@timestamp': expect.any(String), + message: { + role: MessageRole.User, + name: 'get_connectors', + content: JSON.stringify({ connectors: [{ id: 'connector_1' }] }), + }, + }, +]; + describe('observabilityAIAssistant rule_connector', () => { describe('getObsAIAssistantConnectorAdapter', () => { it('uses correct connector_id', () => { @@ -42,7 +101,7 @@ describe('observabilityAIAssistant rule_connector', () => { expect(params).toEqual({ connector: '.azure', - message: 'hello', + prompts: [{ message: 'hello', statuses: ALERT_STATUSES }], rule: { id: 'foo', name: 'bar', tags: [], ruleUrl: 'http://myrule.com' }, alerts: { new: [{ _id: 'new_alert' }], @@ -52,11 +111,85 @@ describe('observabilityAIAssistant rule_connector', () => { }); }); - describe('getObsAIAssistantConnectorType', () => { - it('is correctly configured', () => { - const initResources = jest - .fn() - .mockResolvedValue({} as ObservabilityAIAssistantRouteHandlerResources); + describe('Connector Type - getObsAIAssistantConnectorType', () => { + const completeMock = jest.fn().mockReturnValue(new Observable()); + + const initResources = jest.fn().mockResolvedValue({ + service: { + getClient: async () => ({ complete: completeMock }), + getFunctionClient: async () => ({ + getFunctions: () => [], + getInstructions: () => [], + getAdhocInstructions: () => [], + }), + }, + context: {}, + plugins: { + core: { + start: () => + Promise.resolve({ + http: { basePath: { publicBaseUrl: 'http://kibana.com' } }, + }), + }, + actions: { + start: async () => { + return { + getActionsClientWithRequest: jest.fn().mockResolvedValue({ + async getAll() { + return [{ id: 'connector_1' }]; + }, + }), + }; + }, + }, + }, + } as unknown as ObservabilityAIAssistantRouteHandlerResources); + + const adapter = getObsAIAssistantConnectorAdapter(); + const buildActionParams = (params: { + connector: string; + message?: string; + prompts?: Array<{ message: string; statuses: string[] }>; + }) => { + return adapter.buildActionParams({ + params, + rule: { id: 'foo', name: 'bar', tags: [], consumer: '', producer: '' }, + spaceId: 'default', + alerts: { + all: { count: 1, data: [] }, + new: { + count: 1, + data: [ + { + '@timestamp': new Date().toISOString(), + _id: 'new_alert', + _index: 'alert_index', + 'kibana.alert.instance.id': 'instance_id', + 'kibana.alert.rule.category': 'rule_category', + 'kibana.alert.rule.consumer': 'rule_consumer', + 'kibana.alert.rule.name': 'rule_name', + 'kibana.alert.rule.producer': 'rule_producer', + 'kibana.alert.rule.revision': 1, + 'kibana.alert.rule.tags': [], + 'kibana.alert.rule.rule_type_id': 'rule_type_id', + 'kibana.alert.uuid': 'alert_uuid', + 'kibana.alert.rule.uuid': 'rule_uuid', + 'kibana.alert.start': new Date().toISOString(), + 'kibana.alert.status': ALERT_STATUS_ACTIVE, + 'kibana.space_ids': ['default'], + }, + ], + }, + ongoing: { count: 1, data: [] }, + recovered: { count: 0, data: [] }, + }, + }); + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should have the correct configuration', () => { const connectorType = getObsAIAssistantConnectorType( initResources, new AlertDetailsContextualInsightsService() @@ -66,10 +199,7 @@ describe('observabilityAIAssistant rule_connector', () => { expect(connectorType.minimumLicenseRequired).toEqual('enterprise'); }); - it('does not execute when no new or recovered alerts', async () => { - const initResources = jest - .fn() - .mockResolvedValue({} as ObservabilityAIAssistantRouteHandlerResources); + it('should not execute when there are no new or recovered alerts', async () => { const connectorType = getObsAIAssistantConnectorType( initResources, new AlertDetailsContextualInsightsService() @@ -83,38 +213,40 @@ describe('observabilityAIAssistant rule_connector', () => { expect(initResources).not.toHaveBeenCalled(); }); - it('calls complete api', async () => { - const completeMock = jest.fn().mockReturnValue(new Observable()); - const initResources = jest.fn().mockResolvedValue({ - service: { - getClient: async () => ({ complete: completeMock }), - getFunctionClient: async () => ({ - getFunctions: () => [], - getInstructions: () => [], - getAdhocInstructions: () => [], - }), - }, - context: {}, - plugins: { - core: { - start: () => - Promise.resolve({ - http: { basePath: { publicBaseUrl: 'http://kibana.com' } }, - }), - }, - actions: { - start: async () => { - return { - getActionsClientWithRequest: jest.fn().mockResolvedValue({ - async getAll() { - return [{ id: 'connector_1' }]; - }, - }), - }; - }, - }, - }, - } as unknown as ObservabilityAIAssistantRouteHandlerResources); + it('should call the complete API with a single message', async () => { + const message = 'hello'; + const params = buildActionParams({ connector: 'azure-open-ai', message }); + const connectorType = getObsAIAssistantConnectorType( + initResources, + new AlertDetailsContextualInsightsService() + ); + const result = await connectorType.executor({ + actionId: 'observability-ai-assistant', + request: getFakeKibanaRequest({ id: 'foo', api_key: 'bar' }), + params, + } as unknown as ObsAIAssistantConnectorTypeExecutorOptions); + + expect(result).toEqual({ actionId: 'observability-ai-assistant', status: 'ok' }); + expect(initResources).toHaveBeenCalledTimes(1); + expect(completeMock).toHaveBeenCalledTimes(1); + + expect(completeMock).toHaveBeenCalledWith( + expect.objectContaining({ + persist: true, + isPublic: true, + connectorId: 'azure-open-ai', + kibanaPublicUrl: 'http://kibana.com', + messages: buildConversation(message), + }) + ); + }); + + it('executes the complete API with a single prompt', async () => { + const message = 'hello'; + const params = buildActionParams({ + connector: 'azure-open-ai', + prompts: [{ message, statuses: ALERT_STATUSES }], + }); const connectorType = getObsAIAssistantConnectorType( initResources, @@ -123,11 +255,7 @@ describe('observabilityAIAssistant rule_connector', () => { const result = await connectorType.executor({ actionId: 'observability-ai-assistant', request: getFakeKibanaRequest({ id: 'foo', api_key: 'bar' }), - params: { - message: 'hello', - connector: 'azure-open-ai', - alerts: { new: [{ _id: 'new_alert' }], recovered: [] }, - }, + params, } as unknown as ObsAIAssistantConnectorTypeExecutorOptions); expect(result).toEqual({ actionId: 'observability-ai-assistant', status: 'ok' }); @@ -140,62 +268,52 @@ describe('observabilityAIAssistant rule_connector', () => { isPublic: true, connectorId: 'azure-open-ai', kibanaPublicUrl: 'http://kibana.com', - messages: [ - { - '@timestamp': expect.any(String), - message: { - role: MessageRole.System, - content: '', - }, - }, - { - '@timestamp': expect.any(String), - message: { - role: MessageRole.User, - content: 'hello', - }, - }, - { - '@timestamp': expect.any(String), - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: 'get_alerts_context', - arguments: JSON.stringify({}), - trigger: MessageRole.Assistant as const, - }, - }, - }, - { - '@timestamp': expect.any(String), - message: { - role: MessageRole.User, - name: 'get_alerts_context', - content: expect.any(String), - }, - }, - { - '@timestamp': expect.any(String), - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: 'get_connectors', - arguments: JSON.stringify({}), - trigger: MessageRole.Assistant as const, - }, - }, - }, - { - '@timestamp': expect.any(String), - message: { - role: MessageRole.User, - name: 'get_connectors', - content: JSON.stringify({ connectors: [{ id: 'connector_1' }] }), - }, - }, - ], + messages: buildConversation(message), + }) + ); + }); + + it('should call the complete API with multiple prompts', async () => { + const message = 'hello'; + const message2 = 'bye'; + const params = buildActionParams({ + connector: 'azure-open-ai', + prompts: [ + { message, statuses: ALERT_STATUSES }, + { message: message2, statuses: ALERT_STATUSES }, + ], + }); + + const connectorType = getObsAIAssistantConnectorType( + initResources, + new AlertDetailsContextualInsightsService() + ); + const result = await connectorType.executor({ + actionId: 'observability-ai-assistant', + request: getFakeKibanaRequest({ id: 'foo', api_key: 'bar' }), + params, + } as unknown as ObsAIAssistantConnectorTypeExecutorOptions); + + expect(result).toEqual({ actionId: 'observability-ai-assistant', status: 'ok' }); + expect(initResources).toHaveBeenCalledTimes(1); + expect(completeMock).toHaveBeenCalledTimes(2); + + expect(completeMock).toHaveBeenCalledWith( + expect.objectContaining({ + persist: true, + isPublic: true, + connectorId: 'azure-open-ai', + kibanaPublicUrl: 'http://kibana.com', + messages: buildConversation(message), + }) + ); + expect(completeMock).toHaveBeenCalledWith( + expect.objectContaining({ + persist: true, + isPublic: true, + connectorId: 'azure-open-ai', + kibanaPublicUrl: 'http://kibana.com', + messages: buildConversation(message2), }) ); }); diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.ts b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.ts index 7ad9d78889461..207e4e9488354 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.ts +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/rule_connector/index.ts @@ -37,8 +37,13 @@ import { AlertDetailsContextualInsightsService } from '@kbn/observability-plugin import { getSystemMessageFromInstructions } from '@kbn/observability-ai-assistant-plugin/server/service/util/get_system_message_from_instructions'; import { AdHocInstruction } from '@kbn/observability-ai-assistant-plugin/common/types'; import { EXECUTE_CONNECTOR_FUNCTION_NAME } from '@kbn/observability-ai-assistant-plugin/server/functions/execute_connector'; +import { ObservabilityAIAssistantClient } from '@kbn/observability-ai-assistant-plugin/server'; +import { ChatFunctionClient } from '@kbn/observability-ai-assistant-plugin/server/service/chat_function_client'; +import { ActionsClient } from '@kbn/actions-plugin/server'; +import { PublicMethodsOf } from '@kbn/utility-types'; import { convertSchemaToOpenApi } from './convert_schema_to_open_api'; import { OBSERVABILITY_AI_ASSISTANT_CONNECTOR_ID } from '../../common/rule_connector'; +import { ALERT_STATUSES } from '../../common/constants'; const CONNECTOR_PRIVILEGES = ['api:observabilityAIAssistant', 'app:observabilityAIAssistant']; @@ -64,7 +69,16 @@ const connectorParamsSchemas: Record = { const ParamsSchema = schema.object({ connector: schema.string(), - message: schema.string({ minLength: 1 }), + prompts: schema.maybe( + schema.arrayOf( + schema.object({ + statuses: schema.arrayOf(schema.string()), + message: schema.string({ minLength: 1 }), + }) + ) + ), + status: schema.maybe(schema.string()), + message: schema.maybe(schema.string({ minLength: 1 })), // this is a legacy field }); const RuleSchema = schema.object({ @@ -83,7 +97,12 @@ const AlertSummarySchema = schema.object({ const ConnectorParamsSchema = schema.object({ connector: schema.string(), - message: schema.string({ minLength: 1 }), + prompts: schema.arrayOf( + schema.object({ + statuses: schema.arrayOf(schema.string()), + message: schema.string({ minLength: 1 }), + }) + ), rule: RuleSchema, alerts: AlertSummarySchema, }); @@ -140,7 +159,7 @@ function renderParameterTemplates( ): ConnectorParamsType { return { connector: params.connector, - message: params.message, + prompts: params.prompts, rule: params.rule, alerts: params.alerts, }; @@ -151,19 +170,18 @@ async function executor( initResources: (request: KibanaRequest) => Promise, alertDetailsContextService: AlertDetailsContextualInsightsService ): Promise> { - const request = execOptions.request; - const alerts = execOptions.params.alerts; - - if (!request) { - throw new Error('AI Assistant connector requires a kibana request'); - } + const { request, params } = execOptions; - if (alerts.new.length === 0 && alerts.recovered.length === 0) { + if ((params.alerts?.new || []).length === 0 && (params.alerts?.recovered || []).length === 0) { // connector could be executed with only ongoing actions. we use this path as // dedup mechanism to prevent triggering the same worfklow for an ongoing alert return { actionId: execOptions.actionId, status: 'ok' }; } + if (!request) { + throw new Error('AI Assistant connector requires a kibana request'); + } + const resources = await initResources(request); const client = await resources.service.getClient({ request, scopes: ['observability'] }); const functionClient = await resources.service.getFunctionClient({ @@ -177,6 +195,52 @@ async function executor( await resources.plugins.actions.start() ).getActionsClientWithRequest(request); + await Promise.all( + params.prompts.map((prompt) => + executeAlertsChatCompletion( + resources, + prompt, + params, + alertDetailsContextService, + client, + functionClient, + actionsClient, + execOptions.logger + ) + ) + ); + + return { actionId: execOptions.actionId, status: 'ok' }; +} + +async function executeAlertsChatCompletion( + resources: ObservabilityAIAssistantRouteHandlerResources, + prompt: { statuses: string[]; message: string }, + params: ConnectorParamsType, + alertDetailsContextService: AlertDetailsContextualInsightsService, + client: ObservabilityAIAssistantClient, + functionClient: ChatFunctionClient, + actionsClient: PublicMethodsOf, + logger: Logger +): Promise { + const alerts = { + new: [...(params.alerts?.new || [])], + recovered: [...(params.alerts?.recovered || [])], + }; + + if (ALERT_STATUSES.some((status) => prompt.statuses.includes(status))) { + alerts.new = alerts.new.filter((alert) => + prompt.statuses.includes(get(alert, 'kibana.alert.status')) + ); + alerts.recovered = alerts.recovered.filter((alert) => + prompt.statuses.includes(get(alert, 'kibana.alert.status')) + ); + } + + if (alerts.new.length === 0 && alerts.recovered.length === 0) { + return; + } + const connectorsList = await actionsClient.getAll().then((connectors) => { return connectors.map((connector) => { if (connector.actionTypeId in connectorParamsSchemas) { @@ -210,8 +274,8 @@ If available, include the link of the conversation at the end of your answer.` text: dedent( `The execute_connector function can be used to invoke Kibana connectors. To send to the Slack connector, you need the following arguments: - - the "id" of the connector - - the "params" parameter that you will fill with the message + - the "id" of the connector + - the "params" parameter that you will fill with the message Please include both "id" and "params.message" in the function arguments when executing the Slack connector..` ), }; @@ -219,10 +283,10 @@ If available, include the link of the conversation at the end of your answer.` } const alertsContext = await getAlertsContext( - execOptions.params.rule, - execOptions.params.alerts, + params.rule, + alerts, async (alert: Record) => { - const prompt = await alertDetailsContextService.getAlertDetailsContext( + const alertDetailsContext = await alertDetailsContextService.getAlertDetailsContext( { core: resources.context.core, licensing: resources.context.licensing, @@ -235,7 +299,7 @@ If available, include the link of the conversation at the end of your answer.` 'host.name': get(alert, 'host.name'), } ); - return prompt + return alertDetailsContext .map(({ description, data }) => `${description}:\n${JSON.stringify(data, null, 2)}`) .join('\n\n'); } @@ -246,7 +310,7 @@ If available, include the link of the conversation at the end of your answer.` functionClient, persist: true, isPublic: true, - connectorId: execOptions.params.connector, + connectorId: params.connector, signal: new AbortController().signal, kibanaPublicUrl: (await resources.plugins.core.start()).http.basePath.publicBaseUrl, instructions: [backgroundInstruction], @@ -267,7 +331,7 @@ If available, include the link of the conversation at the end of your answer.` '@timestamp': new Date().toISOString(), message: { role: MessageRole.User, - content: execOptions.params.message, + content: prompt.message, }, }, { @@ -323,11 +387,9 @@ If available, include the link of the conversation at the end of your answer.` .pipe(concatenateChatCompletionChunks()) .subscribe({ error: (err) => { - execOptions.logger.error(err); + logger.error(err); }, }); - - return { actionId: execOptions.actionId, status: 'ok' }; } export const getObsAIAssistantConnectorAdapter = (): ConnectorAdapter< @@ -341,7 +403,15 @@ export const getObsAIAssistantConnectorAdapter = (): ConnectorAdapter< buildActionParams: ({ params, rule, ruleUrl, alerts }) => { return { connector: params.connector, - message: params.message, + // Ensure backwards compatibility by using the message field as a prompt if prompts are missing + prompts: params.prompts + ? params.prompts + : [ + { + statuses: ALERT_STATUSES, + message: params.message || '', + }, + ], rule: { id: rule.id, name: rule.name, tags: rule.tags, ruleUrl: ruleUrl ?? null }, alerts: { new: alerts.new.data, diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/tsconfig.json b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/tsconfig.json index 763bf6659170e..fabeb14f8512b 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/tsconfig.json +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/tsconfig.json @@ -83,8 +83,8 @@ "@kbn/charts-theme", "@kbn/ai-assistant-icon", "@kbn/product-doc-base-plugin", + "@kbn/rule-data-utils", + "@kbn/utility-types", ], - "exclude": [ - "target/**/*" - ] + "exclude": ["target/**/*"] } diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx index b1c5da970a78b..e2308f91235c0 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx @@ -26,12 +26,13 @@ import { css } from '@emotion/react'; import { useSearchParams } from 'react-router-dom-v5-compat'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { OnboardingFlowPackageList } from '../packages_list'; +import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; +import { PackageListSearchForm } from '../package_list_search_form/package_list_search_form'; import { Category } from './types'; -import { useCustomCardsForCategory } from './use_custom_cards_for_category'; -import { useVirtualSearchResults } from './use_virtual_search_results'; +import { useCustomCards } from './use_custom_cards'; import { LogoIcon, SupportedLogo } from '../shared/logo_icon'; import { ObservabilityOnboardingAppServices } from '../..'; +import { PackageList } from '../package_list/package_list'; interface UseCaseOption { id: Category; @@ -147,11 +148,25 @@ export const OnboardingFlowForm: FunctionComponent = () => { [] // eslint-disable-line react-hooks/exhaustive-deps ); - const customCards = useCustomCardsForCategory( - createCollectionCardHandler, - searchParams.get('category') as Category | null - ); - const virtualSearchResults = useVirtualSearchResults(); + const featuredCardsForCategoryMap: Record = { + host: ['auto-detect-logs', 'otel-logs'], + kubernetes: ['kubernetes-quick-start', 'otel-kubernetes'], + application: ['apm-virtual', 'otel-virtual', 'synthetics-virtual'], + cloud: ['azure-logs-virtual', 'aws-logs-virtual', 'gcp-logs-virtual'], + }; + const customCards = useCustomCards(createCollectionCardHandler); + const featuredCardsForCategory: IntegrationCardItem[] = customCards.filter((card) => { + const category = searchParams.get('category') as Category; + + if (category === null) { + return false; + } + + const cardList = featuredCardsForCategoryMap[category] ?? []; + + return cardList.includes(card.id); + }); + /** * Cloud deployments have the new Firehose quick start * flow enabled, so the ond card 'epr:awsfirehose' should @@ -272,7 +287,7 @@ export const OnboardingFlowForm: FunctionComponent = () => { {/* Hiding element instead of not rending these elements in order to preload available packages on page load */}
@@ -329,20 +340,12 @@ export const OnboardingFlowForm: FunctionComponent = () => { - card.type === 'virtual' && !card.isCollectionCard - ) - .concat(virtualSearchResults)} + customCards={customCards.filter((card) => !card.isCollectionCard)} excludePackageIdList={searchExcludePackageIdList} - joinCardLists /> diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards.tsx new file mode 100644 index 0000000000000..7e52d917c5bd4 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards.tsx @@ -0,0 +1,411 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { EuiFlexItem } from '@elastic/eui'; +import { reactRouterNavigate, useKibana } from '@kbn/kibana-react-plugin/public'; +import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; +import { useHistory } from 'react-router-dom'; +import { useLocation } from 'react-router-dom-v5-compat'; +import { ObservabilityOnboardingAppServices } from '../..'; +import { LogoIcon } from '../shared/logo_icon'; + +export function useCustomCards( + createCollectionCardHandler: (query: string) => () => void +): IntegrationCardItem[] { + const history = useHistory(); + const location = useLocation(); + const { + services: { + application, + http, + context: { isServerless, isCloud }, + }, + } = useKibana(); + + const getUrlForApp = application?.getUrlForApp; + + const { href: autoDetectUrl } = reactRouterNavigate(history, `/auto-detect/${location.search}`); + const { href: otelLogsUrl } = reactRouterNavigate(history, `/otel-logs/${location.search}`); + const { href: kubernetesUrl } = reactRouterNavigate(history, `/kubernetes/${location.search}`); + const { href: otelKubernetesUrl } = reactRouterNavigate( + history, + `/otel-kubernetes/${location.search}` + ); + const { href: customLogsUrl } = reactRouterNavigate(history, `/customLogs/${location.search}`); + const { href: firehoseUrl } = reactRouterNavigate(history, `/firehose/${location.search}`); + + const apmUrl = `${getUrlForApp?.('apm')}/${isServerless ? 'onboarding' : 'tutorial'}`; + const otelApmUrl = isServerless ? `${apmUrl}?agent=openTelemetry` : apmUrl; + + const firehoseQuickstartCard: IntegrationCardItem = { + id: 'firehose-quick-start', + name: 'firehose-quick-start', + type: 'virtual', + title: i18n.translate('xpack.observability_onboarding.packageList.uploadFileTitle', { + defaultMessage: 'AWS Firehose', + }), + description: i18n.translate( + 'xpack.observability_onboarding.packageList.uploadFileDescription', + { + defaultMessage: 'Collect logs and metrics from Amazon Web Services (AWS).', + } + ), + categories: ['observability'], + icons: [ + { + type: 'svg', + src: 'https://epr.elastic.co/package/awsfirehose/1.1.0/img/logo_firehose.svg', + }, + ], + url: firehoseUrl, + version: '', + integration: '', + isQuickstart: true, + }; + + return [ + { + id: 'auto-detect-logs', + name: 'auto-detect-logs-virtual', + type: 'virtual', + title: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.autoDetectTitle', + { + defaultMessage: 'Elastic Agent: Logs & Metrics', + } + ), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.autoDetectDescription', + { + defaultMessage: 'Scan your host for log files, metrics, auto-install integrations', + } + ), + extraLabelsBadges: [ + + + , + + + , + ], + categories: ['observability'], + icons: [ + { + type: 'eui', + src: 'agentApp', + }, + ], + url: autoDetectUrl, + version: '', + integration: '', + isQuickstart: true, + }, + { + id: 'otel-logs', + name: 'custom-logs-virtual', + type: 'virtual', + title: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.logsOtelTitle', + { + defaultMessage: 'OpenTelemetry: Logs & Metrics', + } + ), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.logsOtelDescription', + { + defaultMessage: + 'Collect logs and host metrics with the Elastic Distro for OTel Collector', + } + ), + extraLabelsBadges: [ + + + , + + + , + ], + categories: ['observability'], + icons: [ + { + type: 'svg', + src: http?.staticAssets.getPluginAssetHref('opentelemetry.svg') ?? '', + }, + ], + url: otelLogsUrl, + version: '', + integration: '', + isQuickstart: true, + }, + { + id: 'kubernetes-quick-start', + name: 'kubernetes-quick-start', + type: 'virtual', + title: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesTitle', + { + defaultMessage: 'Elastic Agent: Logs & Metrics', + } + ), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesDescription', + { + defaultMessage: 'Collect logs and metrics from Kubernetes using Elastic Agent', + } + ), + extraLabelsBadges: [ + + + , + ], + categories: ['observability'], + icons: [ + { + type: 'eui', + src: 'agentApp', + }, + ], + url: kubernetesUrl, + version: '', + integration: '', + isQuickstart: true, + }, + { + id: 'otel-kubernetes', + name: 'otel-kubernetes-virtual', + type: 'virtual', + title: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesOtelTitle', + { + defaultMessage: 'OpenTelemetry: Full Observability', + } + ), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesOtelDescription', + { + defaultMessage: + 'Collect logs, traces and metrics with the Elastic Distro for OTel Collector', + } + ), + extraLabelsBadges: [ + + + , + ], + categories: ['observability'], + icons: [ + { + type: 'svg', + src: http?.staticAssets.getPluginAssetHref('opentelemetry.svg') ?? '', + }, + ], + url: otelKubernetesUrl, + version: '', + integration: '', + isQuickstart: true, + }, + { + id: 'apm-virtual', + type: 'virtual', + title: i18n.translate('xpack.observability_onboarding.useCustomCardsForCategory.apmTitle', { + defaultMessage: 'Elastic APM', + }), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.apmDescription', + { + defaultMessage: 'Collect distributed traces from your applications with Elastic APM', + } + ), + name: 'apm', + categories: ['observability'], + icons: [ + { + type: 'eui', + src: 'apmApp', + }, + ], + url: apmUrl, + version: '', + integration: '', + }, + { + id: 'otel-virtual', + type: 'virtual', + title: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.apmOtelTitle', + { + defaultMessage: 'OpenTelemetry', + } + ), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.apmOtelDescription', + { + defaultMessage: 'Collect distributed traces with OpenTelemetry', + } + ), + name: 'otel', + categories: ['observability'], + icons: [ + { + type: 'svg', + src: http?.staticAssets.getPluginAssetHref('opentelemetry.svg') ?? '', + }, + ], + url: otelApmUrl, + version: '', + integration: '', + }, + { + id: 'synthetics-virtual', + type: 'virtual', + title: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.syntheticsTitle', + { + defaultMessage: 'Synthetic monitor', + } + ), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.syntheticsDescription', + { + defaultMessage: 'Monitor endpoints, pages, and user journeys', + } + ), + name: 'synthetics', + categories: ['observability'], + icons: [ + { + type: 'eui', + src: 'logoUptime', + }, + ], + url: getUrlForApp?.('synthetics') ?? '', + version: '', + integration: '', + }, + { + id: 'azure-logs-virtual', + type: 'virtual', + title: i18n.translate('xpack.observability_onboarding.useCustomCardsForCategory.azureTitle', { + defaultMessage: 'Azure', + }), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.azureDescription', + { + defaultMessage: 'Collect logs from Microsoft Azure', + } + ), + name: 'azure', + categories: ['observability'], + icons: [], + url: 'https://azure.com', + version: '', + integration: '', + isCollectionCard: true, + onCardClick: createCollectionCardHandler('azure'), + }, + { + id: 'aws-logs-virtual', + type: 'virtual', + title: i18n.translate('xpack.observability_onboarding.useCustomCardsForCategory.awsTitle', { + defaultMessage: 'AWS', + }), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.awsDescription', + { + defaultMessage: 'Collect logs from Amazon Web Services (AWS)', + } + ), + name: 'aws', + categories: ['observability'], + icons: [], + url: 'https://aws.com', + version: '', + integration: '', + isCollectionCard: true, + onCardClick: createCollectionCardHandler('aws'), + }, + { + id: 'gcp-logs-virtual', + type: 'virtual', + title: i18n.translate('xpack.observability_onboarding.useCustomCardsForCategory.gcpTitle', { + defaultMessage: 'Google Cloud Platform', + }), + description: i18n.translate( + 'xpack.observability_onboarding.useCustomCardsForCategory.gcpDescription', + { + defaultMessage: 'Collect logs from Google Cloud Platform', + } + ), + name: 'gcp', + categories: ['observability'], + icons: [], + url: '', + version: '', + integration: '', + isCollectionCard: true, + onCardClick: createCollectionCardHandler('gcp'), + }, + { + id: 'upload-file-virtual', + type: 'virtual', + title: i18n.translate('xpack.observability_onboarding.packageList.uploadFileTitle', { + defaultMessage: 'Upload a file', + }), + description: i18n.translate( + 'xpack.observability_onboarding.packageList.uploadFileDescription', + { + defaultMessage: + 'Upload data from a CSV, TSV, JSON or other log file to Elasticsearch for analysis.', + } + ), + name: 'upload-file', + categories: ['observability'], + icons: [ + { + type: 'eui', + src: 'addDataApp', + }, + ], + url: `${getUrlForApp?.('home')}#/tutorial_directory/fileDataViz`, + version: '', + integration: '', + isCollectionCard: false, + }, + { + id: 'custom-logs', + type: 'virtual', + title: 'Stream log files', + description: 'Stream any logs into Elastic in a simple way and explore their data', + name: 'custom-logs-virtual', + categories: ['observability'], + icons: [ + { + type: 'eui', + src: 'filebeatApp', + }, + ], + url: customLogsUrl, + version: '', + integration: '', + }, + /** + * The new Firehose card should only be visible on Cloud + * as Firehose integration requires additional proxy, + * which is not available for on-prem customers. + */ + ...(isCloud ? [firehoseQuickstartCard] : []), + ]; +} + +function ExtraLabelBadgeWrapper({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx deleted file mode 100644 index bac6e05686a58..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.tsx +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { EuiFlexItem } from '@elastic/eui'; -import { reactRouterNavigate, useKibana } from '@kbn/kibana-react-plugin/public'; -import { useHistory } from 'react-router-dom'; -import { useLocation } from 'react-router-dom-v5-compat'; -import { ObservabilityOnboardingAppServices } from '../..'; -import { CustomCard } from '../packages_list/types'; -import { Category } from './types'; -import { LogoIcon } from '../shared/logo_icon'; - -export function useCustomCardsForCategory( - createCollectionCardHandler: (query: string) => () => void, - category: Category | null -): CustomCard[] | undefined { - const history = useHistory(); - const location = useLocation(); - const { - services: { - application, - http, - context: { isServerless }, - }, - } = useKibana(); - const getUrlForApp = application?.getUrlForApp; - - const { href: autoDetectUrl } = reactRouterNavigate(history, `/auto-detect/${location.search}`); - const { href: otelLogsUrl } = reactRouterNavigate(history, `/otel-logs/${location.search}`); - const { href: kubernetesUrl } = reactRouterNavigate(history, `/kubernetes/${location.search}`); - const { href: otelKubernetesUrl } = reactRouterNavigate( - history, - `/otel-kubernetes/${location.search}` - ); - - const apmUrl = `${getUrlForApp?.('apm')}/${isServerless ? 'onboarding' : 'tutorial'}`; - const otelApmUrl = isServerless ? `${apmUrl}?agent=openTelemetry` : apmUrl; - - switch (category) { - case 'host': - return [ - { - id: 'auto-detect-logs', - name: 'auto-detect-logs-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.autoDetectTitle', - { - defaultMessage: 'Elastic Agent: Logs & Metrics', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.autoDetectDescription', - { - defaultMessage: 'Scan your host for log files, metrics, auto-install integrations', - } - ), - extraLabelsBadges: [ - - - , - - - , - ], - categories: ['observability'], - icons: [ - { - type: 'eui', - src: 'agentApp', - }, - ], - url: autoDetectUrl, - version: '', - integration: '', - isQuickstart: true, - }, - { - id: 'otel-logs', - name: 'custom-logs-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.logsOtelTitle', - { - defaultMessage: 'OpenTelemetry: Logs & Metrics', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.logsOtelDescription', - { - defaultMessage: - 'Collect logs and host metrics with the Elastic Distro for OTel Collector', - } - ), - extraLabelsBadges: [ - - - , - - - , - ], - categories: ['observability'], - icons: [ - { - type: 'svg', - src: http?.staticAssets.getPluginAssetHref('opentelemetry.svg') ?? '', - }, - ], - url: otelLogsUrl, - version: '', - integration: '', - isQuickstart: true, - }, - ]; - - case 'kubernetes': - return [ - { - id: 'kubernetes-quick-start', - name: 'kubernetes-quick-start', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesTitle', - { - defaultMessage: 'Elastic Agent: Logs & Metrics', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesDescription', - { - defaultMessage: 'Collect logs and metrics from Kubernetes using Elastic Agent', - } - ), - extraLabelsBadges: [ - - - , - ], - categories: ['observability'], - icons: [ - { - type: 'eui', - src: 'agentApp', - }, - ], - url: kubernetesUrl, - version: '', - integration: '', - isQuickstart: true, - }, - { - id: 'otel-kubernetes', - name: 'otel-kubernetes-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesOtelTitle', - { - defaultMessage: 'OpenTelemetry: Full Observability', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.kubernetesOtelDescription', - { - defaultMessage: - 'Collect logs, traces and metrics with the Elastic Distro for OTel Collector', - } - ), - extraLabelsBadges: [ - - - , - ], - categories: ['observability'], - icons: [ - { - type: 'svg', - src: http?.staticAssets.getPluginAssetHref('opentelemetry.svg') ?? '', - }, - ], - url: otelKubernetesUrl, - version: '', - integration: '', - isQuickstart: true, - }, - ]; - - case 'application': - return [ - { - id: 'apm-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.apmTitle', - { - defaultMessage: 'Elastic APM', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.apmDescription', - { - defaultMessage: 'Collect distributed traces from your applications with Elastic APM', - } - ), - name: 'apm', - categories: ['observability'], - icons: [ - { - type: 'eui', - src: 'apmApp', - }, - ], - url: apmUrl, - version: '', - integration: '', - }, - { - id: 'otel-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.apmOtelTitle', - { - defaultMessage: 'OpenTelemetry', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.apmOtelDescription', - { - defaultMessage: 'Collect distributed traces with OpenTelemetry', - } - ), - name: 'otel', - categories: ['observability'], - icons: [ - { - type: 'svg', - src: http?.staticAssets.getPluginAssetHref('opentelemetry.svg') ?? '', - }, - ], - url: otelApmUrl, - version: '', - integration: '', - }, - { - id: 'synthetics-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.syntheticsTitle', - { - defaultMessage: 'Synthetic monitor', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.syntheticsDescription', - { - defaultMessage: 'Monitor endpoints, pages, and user journeys', - } - ), - name: 'synthetics', - categories: ['observability'], - icons: [ - { - type: 'eui', - src: 'logoUptime', - }, - ], - url: getUrlForApp?.('synthetics') ?? '', - version: '', - integration: '', - }, - ]; - - case 'cloud': - return [ - { - id: 'azure-logs-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.azureTitle', - { - defaultMessage: 'Azure', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.azureDescription', - { - defaultMessage: 'Collect logs from Microsoft Azure', - } - ), - name: 'azure', - categories: ['observability'], - icons: [], - url: 'https://azure.com', - version: '', - integration: '', - isCollectionCard: true, - onCardClick: createCollectionCardHandler('azure'), - }, - { - id: 'aws-logs-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.awsTitle', - { - defaultMessage: 'AWS', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.awsDescription', - { - defaultMessage: 'Collect logs from Amazon Web Services (AWS)', - } - ), - name: 'aws', - categories: ['observability'], - icons: [], - url: 'https://aws.com', - version: '', - integration: '', - isCollectionCard: true, - onCardClick: createCollectionCardHandler('aws'), - }, - { - id: 'gcp-logs-virtual', - type: 'virtual', - title: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.gcpTitle', - { - defaultMessage: 'Google Cloud Platform', - } - ), - description: i18n.translate( - 'xpack.observability_onboarding.useCustomCardsForCategory.gcpDescription', - { - defaultMessage: 'Collect logs from Google Cloud Platform', - } - ), - name: 'gcp', - categories: ['observability'], - icons: [], - url: '', - version: '', - integration: '', - isCollectionCard: true, - onCardClick: createCollectionCardHandler('gcp'), - }, - ]; - - default: - return undefined; - } -} - -function ExtraLabelBadgeWrapper({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_virtual_search_results.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_virtual_search_results.ts deleted file mode 100644 index 688a17528f43a..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/onboarding_flow_form/use_virtual_search_results.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { useHistory } from 'react-router-dom'; -import { reactRouterNavigate, useKibana } from '@kbn/kibana-react-plugin/public'; -import { CustomCard } from '../packages_list/types'; -import { ObservabilityOnboardingAppServices } from '../..'; - -export function useVirtualSearchResults(): CustomCard[] { - const { - services: { - application, - context: { isCloud }, - }, - } = useKibana(); - - const history = useHistory(); - const { href: customLogsUrl } = reactRouterNavigate(history, `/customLogs/${location.search}`); - const { href: firehoseUrl } = reactRouterNavigate(history, `/firehose/${location.search}`); - const getUrlForApp = application?.getUrlForApp; - const firehoseQuickstartCard: CustomCard = { - id: 'firehose-quick-start', - name: 'firehose-quick-start', - type: 'virtual', - title: i18n.translate('xpack.observability_onboarding.packageList.uploadFileTitle', { - defaultMessage: 'AWS Firehose', - }), - description: i18n.translate( - 'xpack.observability_onboarding.packageList.uploadFileDescription', - { - defaultMessage: 'Collect logs and metrics from Amazon Web Services (AWS).', - } - ), - categories: [], - icons: [ - { - type: 'svg', - src: 'https://epr.elastic.co/package/awsfirehose/1.1.0/img/logo_firehose.svg', - }, - ], - url: firehoseUrl, - version: '', - integration: '', - isQuickstart: true, - }; - - return [ - { - id: 'upload-file-virtual', - type: 'virtual', - title: i18n.translate('xpack.observability_onboarding.packageList.uploadFileTitle', { - defaultMessage: 'Upload a file', - }), - description: i18n.translate( - 'xpack.observability_onboarding.packageList.uploadFileDescription', - { - defaultMessage: - 'Upload data from a CSV, TSV, JSON or other log file to Elasticsearch for analysis.', - } - ), - name: 'upload-file', - categories: [], - icons: [ - { - type: 'eui', - src: 'addDataApp', - }, - ], - url: `${getUrlForApp?.('home')}#/tutorial_directory/fileDataViz`, - version: '', - integration: '', - isCollectionCard: false, - }, - { - id: 'custom-logs', - type: 'virtual', - title: 'Stream log files', - description: 'Stream any logs into Elastic in a simple way and explore their data', - name: 'custom-logs-virtual', - categories: ['observability'], - icons: [ - { - type: 'eui', - src: 'filebeatApp', - }, - ], - url: customLogsUrl, - version: '', - integration: '', - }, - /** - * The new Firehose card should only be visible on Cloud - * as Firehose integration requires additional proxy, - * which is not available for on-prem customers. - */ - ...(isCloud ? [firehoseQuickstartCard] : []), - ]; -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list/package_list.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list/package_list.tsx new file mode 100644 index 0000000000000..333fbe4331c4d --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list/package_list.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; +import React, { Suspense, lazy } from 'react'; + +export const LazyPackageList = lazy(async () => ({ + default: await import('@kbn/fleet-plugin/public') + .then((module) => module.PackageList()) + .then((pkg) => pkg.PackageListGrid), +})); + +interface Props { + list: IntegrationCardItem[]; + searchTerm?: string; +} + +export function PackageList({ list, searchTerm = '' }: Props) { + return ( + /** + * Suspense wrapper is required by PackageListGrid, but + * Onboarding does not need it because it pre-loads all + * packages beforehand with a custom loading state. + */ + }> + {}} + setCategory={() => {}} + categories={[]} + setUrlandReplaceHistory={() => {}} + setUrlandPushHistory={() => {}} + showCardLabels={true} + /> + + ); +} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/index.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/package_list_search_form.tsx similarity index 50% rename from x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/index.tsx rename to x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/package_list_search_form.tsx index 3ddaabeaded41..61f7aa69920c5 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/index.tsx +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/package_list_search_form.tsx @@ -9,120 +9,80 @@ import type { AvailablePackagesHookType, IntegrationCardItem } from '@kbn/fleet- import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiButton, EuiCallOut, EuiSearchBar, EuiSkeletonText } from '@elastic/eui'; -import React, { useRef, Suspense, useEffect } from 'react'; +import React, { useRef, useMemo } from 'react'; import useAsyncRetry from 'react-use/lib/useAsyncRetry'; -import { PackageList, fetchAvailablePackagesHook } from './lazy'; -import { useIntegrationCardList } from './use_integration_card_list'; -import { CustomCard } from './types'; +import { useCardUrlRewrite } from './use_card_url_rewrite'; +import { PackageList } from '../package_list/package_list'; interface Props { + searchQuery: string; + setSearchQuery: (searchQuery: string) => void; /** - * A subset of either existing card names to feature, or virtual - * cards to display. The inclusion of CustomCards will override the default - * list functionality. - */ - customCards?: CustomCard[]; - /** - * Override the default `observability` option. + * A list of custom items to include into the package list */ + customCards?: IntegrationCardItem[]; selectedCategory?: string[]; - showSearchBar?: boolean; packageListRef?: React.Ref; - searchQuery?: string; - setSearchQuery?: React.Dispatch>; flowCategory?: string | null; - flowSearch?: string; - /** - * When enabled, custom and integration cards are joined into a single list. - */ - joinCardLists?: boolean; excludePackageIdList?: string[]; - onLoaded?: () => void; } type WrapperProps = Props & { useAvailablePackages: AvailablePackagesHookType; }; -const Loading = () => ; +const fetchAvailablePackagesHook = (): Promise => + import('@kbn/fleet-plugin/public') + .then((module) => module.AvailablePackagesHook()) + .then((hook) => hook.useAvailablePackages); + +const Loading = () => ; const PackageListGridWrapper = ({ - selectedCategory = ['observability', 'os_system'], useAvailablePackages, - showSearchBar = false, packageListRef, searchQuery, setSearchQuery, customCards, flowCategory, - flowSearch, - joinCardLists = false, excludePackageIdList = [], - onLoaded, }: WrapperProps) => { - const { filteredCards, isLoading } = useAvailablePackages({ + const { filteredCards: integrationCards, isLoading } = useAvailablePackages({ prereleaseIntegrationsEnabled: false, }); - - const list: IntegrationCardItem[] = useIntegrationCardList( - filteredCards, - selectedCategory, - excludePackageIdList, - customCards, - flowCategory, - flowSearch, - joinCardLists - ); - - useEffect(() => { - if (!isLoading && onLoaded !== undefined) { - onLoaded(); - } - }, [isLoading, onLoaded]); + const rewriteUrl = useCardUrlRewrite({ category: flowCategory, search: searchQuery }); + + const list: IntegrationCardItem[] = useMemo(() => { + return (customCards ?? []) + .concat(integrationCards) + .filter((card) => + card.categories.some((category) => ['observability', 'os_system'].includes(category)) + ) + .filter((card) => !excludePackageIdList.includes(card.id)) + .map(rewriteUrl); + }, [customCards, excludePackageIdList, integrationCards, rewriteUrl]); if (isLoading) return ; - const showPackageList = (showSearchBar && !!searchQuery) || showSearchBar === false; - return ( - }> -
- {showSearchBar && ( - { - if (error) return; - - setSearchQuery?.(queryText); - }} - query={searchQuery ?? ''} - /> - )} - {showPackageList && ( - {}} - setCategory={() => {}} - categories={[]} - setUrlandReplaceHistory={() => {}} - setUrlandPushHistory={() => {}} - showCardLabels={true} - /> - )} -
-
+
+ { + if (error) return; + + setSearchQuery(queryText); + }} + query={searchQuery} + /> + {searchQuery !== '' && } +
); }; -const WithAvailablePackages = React.forwardRef( +export const PackageListSearchForm = React.forwardRef( (props: Props, packageListRef?: React.Ref) => { const ref = useRef(null); @@ -176,5 +136,3 @@ const WithAvailablePackages = React.forwardRef( ); } ); - -export { WithAvailablePackages as OnboardingFlowPackageList }; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/use_integration_card_list.test.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts similarity index 96% rename from x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/use_integration_card_list.test.ts rename to x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts index cb86eebf8bb5a..b0746f798f309 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/use_integration_card_list.test.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { addPathParamToUrl, toOnboardingPath } from './use_integration_card_list'; +import { addPathParamToUrl, toOnboardingPath } from './use_card_url_rewrite'; describe('useIntegratrionCardList', () => { describe('toOnboardingPath', () => { diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.ts new file mode 100644 index 0000000000000..ce462566bc4d1 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/package_list_search_form/use_card_url_rewrite.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 { useMemo } from 'react'; +import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; + +export function toOnboardingPath({ + basePath, + category, + search, +}: { + basePath?: string; + category?: string | null; + search?: string; +}): string | null { + if (typeof basePath !== 'string' && !basePath) return null; + const path = `${basePath}/app/observabilityOnboarding`; + if (!category && !search) return path; + const params = new URLSearchParams(); + if (category) params.append('category', category); + if (search) params.append('search', search); + return `${path}?${params.toString()}`; +} + +export function addPathParamToUrl(url: string, onboardingLink: string) { + const encoded = encodeURIComponent(onboardingLink); + if (url.indexOf('?') >= 0) { + return `${url}&observabilityOnboardingLink=${encoded}`; + } + return `${url}?observabilityOnboardingLink=${encoded}`; +} + +export function useCardUrlRewrite(props: { category?: string | null; search?: string }) { + const kibana = useKibana(); + const basePath = kibana.services.http?.basePath.get(); + const onboardingLink = useMemo(() => toOnboardingPath({ basePath, ...props }), [basePath, props]); + return (card: IntegrationCardItem) => ({ + ...card, + url: + card.url.indexOf('/app/integrations') >= 0 && onboardingLink + ? addPathParamToUrl(card.url, onboardingLink) + : card.url, + }); +} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/lazy.tsx b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/lazy.tsx deleted file mode 100644 index e273041d1843f..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/lazy.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { AvailablePackagesHookType } from '@kbn/fleet-plugin/public'; -import { lazy } from 'react'; - -export const PackageList = lazy(async () => ({ - default: await import('@kbn/fleet-plugin/public') - .then((module) => module.PackageList()) - .then((pkg) => pkg.PackageListGrid), -})); - -export const fetchAvailablePackagesHook = (): Promise => - import('@kbn/fleet-plugin/public') - .then((module) => module.AvailablePackagesHook()) - .then((hook) => hook.useAvailablePackages); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/use_integration_card_list.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/use_integration_card_list.ts deleted file mode 100644 index c0a6686cfaf12..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/application/packages_list/use_integration_card_list.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import { IntegrationCardItem } from '@kbn/fleet-plugin/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { CustomCard } from './types'; - -export function toOnboardingPath({ - basePath, - category, - search, -}: { - basePath?: string; - category?: string | null; - search?: string; -}): string | null { - if (typeof basePath !== 'string' && !basePath) return null; - const path = `${basePath}/app/observabilityOnboarding`; - if (!category && !search) return path; - const params = new URLSearchParams(); - if (category) params.append('category', category); - if (search) params.append('search', search); - return `${path}?${params.toString()}`; -} - -export function addPathParamToUrl(url: string, onboardingLink: string) { - const encoded = encodeURIComponent(onboardingLink); - if (url.indexOf('?') >= 0) { - return `${url}&observabilityOnboardingLink=${encoded}`; - } - return `${url}?observabilityOnboardingLink=${encoded}`; -} - -function useCardUrlRewrite(props: { category?: string | null; search?: string }) { - const kibana = useKibana(); - const basePath = kibana.services.http?.basePath.get(); - const onboardingLink = useMemo(() => toOnboardingPath({ basePath, ...props }), [basePath, props]); - return (card: IntegrationCardItem) => ({ - ...card, - url: - card.url.indexOf('/app/integrations') >= 0 && onboardingLink - ? addPathParamToUrl(card.url, onboardingLink) - : card.url, - }); -} - -function extractFeaturedCards(filteredCards: IntegrationCardItem[], featuredCardNames?: string[]) { - const featuredCards: Record = {}; - filteredCards.forEach((card) => { - if (featuredCardNames?.includes(card.name)) { - featuredCards[card.name] = card; - } - }); - return featuredCards; -} - -function formatCustomCards( - rewriteUrl: (card: IntegrationCardItem) => IntegrationCardItem, - customCards: CustomCard[], - featuredCards: Record -) { - const cards: IntegrationCardItem[] = []; - for (const card of customCards) { - if (card.type === 'featured' && !!featuredCards[card.name]) { - cards.push(rewriteUrl(featuredCards[card.name]!)); - } else if (card.type === 'virtual') { - cards.push(rewriteUrl(card)); - } - } - return cards; -} - -function useFilteredCards( - rewriteUrl: (card: IntegrationCardItem) => IntegrationCardItem, - integrationsList: IntegrationCardItem[], - selectedCategory: string[], - excludePackageIdList: string[], - customCards?: CustomCard[] -) { - return useMemo(() => { - const integrationCards = integrationsList - .filter((card) => !excludePackageIdList.includes(card.id)) - .filter((card) => card.categories.some((category) => selectedCategory.includes(category))) - .map(rewriteUrl); - - if (!customCards) { - return { featuredCards: {}, integrationCards }; - } - - return { - featuredCards: extractFeaturedCards( - integrationsList, - customCards.filter((c) => c.type === 'featured').map((c) => c.name) - ), - integrationCards, - }; - }, [integrationsList, rewriteUrl, customCards, excludePackageIdList, selectedCategory]); -} - -/** - * Formats the cards to display on the integration list. - * @param integrationsList the list of cards from the integrations API. - * @param selectedCategory the card category to filter by. - * @param customCards any virtual or featured cards. - * @param fullList when true all integration cards are included. - * @returns the list of cards to display. - */ -export function useIntegrationCardList( - integrationsList: IntegrationCardItem[], - selectedCategory: string[], - excludePackageIdList: string[], - customCards?: CustomCard[], - flowCategory?: string | null, - flowSearch?: string, - fullList = false -): IntegrationCardItem[] { - const rewriteUrl = useCardUrlRewrite({ category: flowCategory, search: flowSearch }); - const { featuredCards, integrationCards } = useFilteredCards( - rewriteUrl, - integrationsList, - selectedCategory, - excludePackageIdList, - customCards - ); - - if (customCards && customCards.length > 0) { - const formattedCustomCards = formatCustomCards(rewriteUrl, customCards, featuredCards); - if (fullList) { - return [...formattedCustomCards, ...integrationCards] as IntegrationCardItem[]; - } - return formattedCustomCards; - } - return integrationCards; -} diff --git a/x-pack/solutions/observability/plugins/profiling/server/lib/setup/get_has_setup_privileges.ts b/x-pack/solutions/observability/plugins/profiling/server/lib/setup/get_has_setup_privileges.ts index ad4a0879d9c88..bc9fe8d419589 100644 --- a/x-pack/solutions/observability/plugins/profiling/server/lib/setup/get_has_setup_privileges.ts +++ b/x-pack/solutions/observability/plugins/profiling/server/lib/setup/get_has_setup_privileges.ts @@ -7,7 +7,7 @@ import type { KibanaRequest } from '@kbn/core/server'; import { INTEGRATIONS_PLUGIN_ID, PLUGIN_ID as FLEET_PLUGIN_ID } from '@kbn/fleet-plugin/common'; -import { ApiOperation } from '@kbn/security-plugin-types-server'; +import { ApiOperation } from '@kbn/security-plugin-types-common'; import type { ProfilingPluginStartDeps } from '../../types'; export async function getHasSetupPrivileges({ diff --git a/x-pack/solutions/observability/plugins/profiling/tsconfig.json b/x-pack/solutions/observability/plugins/profiling/tsconfig.json index bcb0ced0afdc5..439fb920fa44f 100644 --- a/x-pack/solutions/observability/plugins/profiling/tsconfig.json +++ b/x-pack/solutions/observability/plugins/profiling/tsconfig.json @@ -55,7 +55,7 @@ "@kbn/deeplinks-observability", "@kbn/react-kibana-context-render", "@kbn/apm-data-access-plugin", - "@kbn/security-plugin-types-server" + "@kbn/security-plugin-types-common" // add references to other TypeScript projects the plugin depends on // requiredPlugins from ./kibana.json diff --git a/x-pack/solutions/observability/plugins/slo/common/locators/paths.ts b/x-pack/solutions/observability/plugins/slo/common/locators/paths.ts index 3f3d53dcb6345..654be4bf9ebe3 100644 --- a/x-pack/solutions/observability/plugins/slo/common/locators/paths.ts +++ b/x-pack/solutions/observability/plugins/slo/common/locators/paths.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; + export const SLOS_BASE_PATH = '/app/slos'; export const SLOS_PATH = '/' as const; export const SLOS_WELCOME_PATH = '/welcome' as const; @@ -25,9 +27,9 @@ export const paths = { sloEdit: (sloId: string) => `${SLOS_BASE_PATH}/edit/${encodeURIComponent(sloId)}`, sloEditWithEncodedForm: (sloId: string, encodedParams: string) => `${SLOS_BASE_PATH}/edit/${encodeURIComponent(sloId)}?_a=${encodedParams}`, - sloDetails: (sloId: string, instanceId?: string, remoteName?: string, tabId?: string) => { + sloDetails: (sloId: string, instanceId: string, remoteName?: string, tabId?: string) => { const qs = new URLSearchParams(); - if (!!instanceId && instanceId !== '*') qs.append('instanceId', instanceId); + if (instanceId !== ALL_VALUE) qs.append('instanceId', instanceId); if (!!remoteName) qs.append('remoteName', remoteName); if (tabId) { return `${SLOS_BASE_PATH}/${encodeURIComponent(sloId)}/${tabId}?${qs.toString()}`; diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/configuration.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/configuration.tsx index 55305f076d893..49bb4e0d46463 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/configuration.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/burn_rate/configuration.tsx @@ -22,7 +22,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ALL_VALUE } from '@kbn/slo-schema'; import React, { useState } from 'react'; import { SloSelector } from '../alerts/slo_selector'; import type { EmbeddableProps } from './types'; @@ -76,7 +75,7 @@ export function Configuration({ onCreate, onCancel }: Props) { onSelected={(slo) => { setHasError(slo === undefined); if (slo && 'id' in slo) { - setSelectedSlo({ sloId: slo.id, sloInstanceId: slo.instanceId ?? ALL_VALUE }); + setSelectedSlo({ sloId: slo.id, sloInstanceId: slo.instanceId }); } }} /> diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview.tsx index 40f72b7915c7b..2efa6cf2b30ed 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview.tsx @@ -5,20 +5,20 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; -import { i18n } from '@kbn/i18n'; import { EuiLoadingChart } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import React, { useEffect, useState } from 'react'; import { Subject } from 'rxjs'; -import { SloOverviewDetails } from '../common/slo_overview_details'; -import { formatHistoricalData } from '../../../utils/slo/chart_data_formatter'; -import { useFetchHistoricalSummary } from '../../../hooks/use_fetch_historical_summary'; import { useFetchActiveAlerts } from '../../../hooks/use_fetch_active_alerts'; +import { useFetchHistoricalSummary } from '../../../hooks/use_fetch_historical_summary'; import { useFetchRulesForSlo } from '../../../hooks/use_fetch_rules_for_slo'; -import { SloCardItemBadges } from '../../../pages/slos/components/card_view/slo_card_item_badges'; -import { SloCardChart } from '../../../pages/slos/components/card_view/slo_card_item'; import { useFetchSloDetails } from '../../../hooks/use_fetch_slo_details'; +import { SloCardChart } from '../../../pages/slos/components/card_view/slo_card_item'; +import { SloCardItemBadges } from '../../../pages/slos/components/card_view/slo_card_item_badges'; +import { formatHistoricalData } from '../../../utils/slo/chart_data_formatter'; +import { SloOverviewDetails } from '../common/slo_overview_details'; import { SingleSloCustomInput } from './types'; @@ -54,7 +54,7 @@ export function SloOverview({ sloId, sloInstanceId, remoteName, reloadSubject }: }); const { data: activeAlertsBySlo } = useFetchActiveAlerts({ - sloIdsAndInstanceIds: slo ? [[slo.id, slo.instanceId ?? ALL_VALUE] as [string, string]] : [], + sloIdsAndInstanceIds: slo ? [[slo.id, slo.instanceId] as [string, string]] : [], }); const { data: historicalSummaries = [] } = useFetchHistoricalSummary({ @@ -96,8 +96,7 @@ export function SloOverview({ sloId, sloInstanceId, remoteName, reloadSubject }: const activeAlerts = activeAlertsBySlo.get(slo); const historicalSummary = historicalSummaries.find( - (histSummary) => - histSummary.sloId === slo.id && histSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + (histSummary) => histSummary.sloId === slo.id && histSummary.instanceId === slo.instanceId )?.data; const historicalSliData = formatHistoricalData(historicalSummary, 'sli_value'); diff --git a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview_grid.tsx b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview_grid.tsx index 6334eb7e430b2..fcf8d44232cbc 100644 --- a/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview_grid.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/embeddable/slo/overview/slo_overview_grid.tsx @@ -114,7 +114,7 @@ export function SloCardChartList({ sloId }: { sloId: string }) { const historicalSummary = historicalSummaries.find( - (hist) => hist.sloId === slo.id && hist.instanceId === (slo.instanceId ?? ALL_VALUE) + (hist) => hist.sloId === slo.id && hist.instanceId === slo.instanceId )?.data ?? []; const lastArray = chartsData[chartsData.length - 1]; diff --git a/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_active_alerts.ts b/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_active_alerts.ts index 8ca275ae78a1a..75c35651c26b4 100644 --- a/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_active_alerts.ts +++ b/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_active_alerts.ts @@ -12,10 +12,10 @@ import { AlertConsumers, SLO_RULE_TYPE_IDS, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; +import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; import { useKibana } from './use_kibana'; import { sloKeys } from './query_key_factory'; import { ActiveAlerts } from './active_alerts'; - import { SLO_LONG_REFETCH_INTERVAL } from '../constants'; type SloIdAndInstanceId = [string, string]; @@ -83,7 +83,9 @@ export function useFetchActiveAlerts({ bool: { filter: [ { term: { 'slo.id': sloId } }, - ...(instanceId === '*' ? [] : [{ term: { 'slo.instanceId': instanceId } }]), + ...(instanceId === ALL_VALUE + ? [] + : [{ term: { 'slo.instanceId': instanceId } }]), ], }, })), diff --git a/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_slo_details.ts b/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_slo_details.ts index b32c0c6dc5976..232a47eac832b 100644 --- a/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_slo_details.ts +++ b/x-pack/solutions/observability/plugins/slo/public/hooks/use_fetch_slo_details.ts @@ -50,7 +50,7 @@ export function useFetchSloDetails({ params: { path: { id: sloId! }, query: { - ...(!!instanceId && instanceId !== ALL_VALUE && { instanceId }), + ...(instanceId !== ALL_VALUE && { instanceId }), ...(remoteName && { remoteName }), }, }, diff --git a/x-pack/solutions/observability/plugins/slo/public/locators/slo_details.ts b/x-pack/solutions/observability/plugins/slo/public/locators/slo_details.ts index c3814a3653176..4013cffe87aa6 100644 --- a/x-pack/solutions/observability/plugins/slo/public/locators/slo_details.ts +++ b/x-pack/solutions/observability/plugins/slo/public/locators/slo_details.ts @@ -8,6 +8,7 @@ import type { SerializableRecord } from '@kbn/utility-types'; import type { LocatorDefinition } from '@kbn/share-plugin/public'; import { sloDetailsLocatorID } from '@kbn/observability-plugin/common'; +import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; export interface SloDetailsLocatorParams extends SerializableRecord { sloId?: string; @@ -19,7 +20,9 @@ export class SloDetailsLocatorDefinition implements LocatorDefinition { const queryParams = - !!instanceId && instanceId !== '*' ? `?instanceId=${encodeURIComponent(instanceId)}` : ''; + !!instanceId && instanceId !== ALL_VALUE + ? `?instanceId=${encodeURIComponent(instanceId)}` + : ''; const path = !!sloId ? `/${encodeURIComponent(sloId)}${queryParams}` : '/'; return { diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/groupings/slo_grouping_value_selector.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/groupings/slo_grouping_value_selector.tsx index f53acffc12625..1de2c3197b149 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/groupings/slo_grouping_value_selector.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/groupings/slo_grouping_value_selector.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import React, { useEffect, useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; import useDebounce from 'react-use/lib/useDebounce'; @@ -48,7 +48,7 @@ export function SLOGroupingValueSelector({ slo, groupingKey, value }: Props) { const { isLoading, isError, data } = useFetchSloGroupings({ sloId: slo.id, groupingKey, - instanceId: slo.instanceId ?? ALL_VALUE, + instanceId: slo.instanceId, search: debouncedSearch, remoteName, }); diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/historical_data_charts.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/historical_data_charts.tsx index da766b765cac7..1758615fd70f3 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/historical_data_charts.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/historical_data_charts.tsx @@ -4,15 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { EuiFlexItem } from '@elastic/eui'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import React from 'react'; -import { TimeBounds } from '../types'; import { useFetchHistoricalSummary } from '../../../hooks/use_fetch_historical_summary'; import { formatHistoricalData } from '../../../utils/slo/chart_data_formatter'; -import { SloTabId } from './slo_details'; -import { SliChartPanel } from './sli_chart_panel'; +import { TimeBounds } from '../types'; import { ErrorBudgetChartPanel } from './error_budget_chart_panel'; +import { SliChartPanel } from './sli_chart_panel'; +import { SloTabId } from './slo_details'; export interface Props { slo: SLOWithSummaryResponse; @@ -38,8 +38,7 @@ export function HistoricalDataCharts({ const sloHistoricalSummary = historicalSummaries.find( (historicalSummary) => - historicalSummary.sloId === slo.id && - historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + historicalSummary.sloId === slo.id && historicalSummary.instanceId === slo.instanceId ); const errorBudgetBurnDownData = formatHistoricalData( diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/slo_detail_alerts.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/slo_detail_alerts.tsx index 7b31baf0d62b0..1f59818f152a7 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/slo_detail_alerts.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/components/slo_detail_alerts.tsx @@ -5,11 +5,11 @@ * 2.0. */ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import React, { Fragment } from 'react'; import { AlertConsumers, SLO_RULE_TYPE_IDS } from '@kbn/rule-data-utils'; +import React, { Fragment } from 'react'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { SLO_ALERTS_TABLE_ID } from '@kbn/observability-shared-plugin/common'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useKibana } from '../../../hooks/use_kibana'; export interface Props { @@ -38,7 +38,7 @@ export function SloDetailsAlerts({ slo }: Props) { bool: { filter: [ { term: { 'slo.id': slo.id } }, - { term: { 'slo.instanceId': slo.instanceId ?? ALL_VALUE } }, + { term: { 'slo.instanceId': slo.instanceId } }, ], }, }} diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_actions.ts b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_actions.ts index 8f3b6ad599419..b642b044fbcb6 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_actions.ts +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_actions.ts @@ -6,7 +6,7 @@ */ import { rulesLocatorID, RulesParams } from '@kbn/observability-plugin/public'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import path from 'path'; import { paths } from '../../../../common/locators/paths'; @@ -77,11 +77,7 @@ export const useSloActions = ({ } }; - const detailsUrl = paths.sloDetails( - slo.id, - ![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined, - slo.remote?.remoteName - ); + const detailsUrl = paths.sloDetails(slo.id, slo.instanceId, slo.remote?.remoteName); const remoteDeleteUrl = createRemoteSloDeleteUrl(slo, spaceId); const remoteResetUrl = createRemoteSloResetUrl(slo, spaceId); diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_details_tabs.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_details_tabs.tsx index 4b7389e969f32..9c08c34f8f5a8 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_details_tabs.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_details/hooks/use_slo_details_tabs.tsx @@ -7,7 +7,7 @@ import { EuiNotificationBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import React from 'react'; import { paths } from '../../../../common/locators/paths'; import { useFetchActiveAlerts } from '../../../hooks/use_fetch_active_alerts'; @@ -31,7 +31,7 @@ export const useSloDetailsTabs = ({ setSelectedTabId?: (val: SloTabId) => void; }) => { const { data: activeAlerts } = useFetchActiveAlerts({ - sloIdsAndInstanceIds: slo ? [[slo.id, slo.instanceId ?? ALL_VALUE]] : [], + sloIdsAndInstanceIds: slo ? [[slo.id, slo.instanceId]] : [], shouldRefetch: isAutoRefreshing, }); diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/slo_edit.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/slo_edit.tsx index 0a563bbe75b44..a8feecfb2b023 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slo_edit/slo_edit.tsx @@ -45,7 +45,7 @@ export function SloEditPage() { ...(!!slo ? [ { - href: basePath.prepend(paths.sloDetails(slo!.id)), + href: basePath.prepend(paths.sloDetails(slo.id, slo.instanceId)), text: slo!.name, }, ] diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/card_view/slo_list_card_view.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/card_view/slo_list_card_view.tsx index 05ca63fcb8cef..d0cff25edeb52 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/card_view/slo_list_card_view.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/card_view/slo_list_card_view.tsx @@ -13,7 +13,7 @@ import { useIsWithinBreakpoints, } from '@elastic/eui'; import { EuiFlexGridProps } from '@elastic/eui/src/components/flex/flex_grid'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import React from 'react'; import { useFetchActiveAlerts } from '../../../../hooks/use_fetch_active_alerts'; import { useFetchHistoricalSummary } from '../../../../hooks/use_fetch_historical_summary'; @@ -44,9 +44,7 @@ const useColumns = () => { }; export function SloListCardView({ sloList, loading, error }: Props) { - const sloIdsAndInstanceIds = sloList.map( - (slo) => [slo.id, slo.instanceId ?? ALL_VALUE] as [string, string] - ); + const sloIdsAndInstanceIds = sloList.map((slo) => [slo.id, slo.instanceId] as [string, string]); const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIdsAndInstanceIds }); const { data: rulesBySlo, refetchRules } = useFetchRulesForSlo({ sloIds: sloIdsAndInstanceIds.map((item) => item[0]), @@ -67,7 +65,7 @@ export function SloListCardView({ sloList, loading, error }: Props) { {sloList .filter((slo) => slo.summary) .map((slo) => ( - + historicalSummary.sloId === slo.id && - historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + historicalSummary.instanceId === slo.instanceId )?.data } historicalSummaryLoading={historicalSummaryLoading} diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/compact_view/slo_list_compact_view.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/compact_view/slo_list_compact_view.tsx index 5e4584105c1a3..c1ed5e8b852d1 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/compact_view/slo_list_compact_view.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/compact_view/slo_list_compact_view.tsx @@ -76,9 +76,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { const spaceId = useSpace(); const percentFormat = uiSettings.get('format:percent:defaultPattern'); - const sloIdsAndInstanceIds = sloList.map( - (slo) => [slo.id, slo.instanceId ?? ALL_VALUE] as [string, string] - ); + const sloIdsAndInstanceIds = sloList.map((slo) => [slo.id, slo.instanceId] as [string, string]); const { data: permissions } = usePermissions(); const filteredRuleTypes = useGetFilteredRuleTypes(); @@ -182,13 +180,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { }), onClick: (slo: SLOWithSummaryResponse) => { const sloDetailsUrl = basePath.prepend( - paths.sloDetails( - slo.id, - ![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId - ? slo.instanceId - : undefined, - slo.remote?.remoteName - ) + paths.sloDetails(slo.id, slo.instanceId, slo.remote?.remoteName) ); navigateToUrl(sloDetailsUrl); }, @@ -395,13 +387,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { 'data-test-subj': 'sloItem', render: (_, slo: SLOWithSummaryResponse) => { const sloDetailsUrl = basePath.prepend( - paths.sloDetails( - slo.id, - ![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId - ? slo.instanceId - : undefined, - slo.remote?.remoteName - ) + paths.sloDetails(slo.id, slo.instanceId, slo.remote?.remoteName) ); return ( @@ -453,8 +439,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { const historicalSliData = formatHistoricalData( historicalSummaries.find( (historicalSummary) => - historicalSummary.sloId === slo.id && - historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + historicalSummary.sloId === slo.id && historicalSummary.instanceId === slo.instanceId )?.data, 'sli_value' ); @@ -487,8 +472,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { const errorBudgetBurnDownData = formatHistoricalData( historicalSummaries.find( (historicalSummary) => - historicalSummary.sloId === slo.id && - historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + historicalSummary.sloId === slo.id && historicalSummary.instanceId === slo.instanceId )?.data, 'error_budget_remaining' ); diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/slo_list_view/slo_list_view.tsx b/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/slo_list_view/slo_list_view.tsx index a97c99d470336..154336bea57fb 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/slo_list_view/slo_list_view.tsx +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slos/components/slo_list_view/slo_list_view.tsx @@ -6,7 +6,7 @@ */ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import React from 'react'; import { useFetchActiveAlerts } from '../../../../hooks/use_fetch_active_alerts'; import { useFetchHistoricalSummary } from '../../../../hooks/use_fetch_historical_summary'; @@ -22,9 +22,7 @@ export interface Props { } export function SloListView({ sloList, loading, error }: Props) { - const sloIdsAndInstanceIds = sloList.map( - (slo) => [slo.id, slo.instanceId ?? ALL_VALUE] as [string, string] - ); + const sloIdsAndInstanceIds = sloList.map((slo) => [slo.id, slo.instanceId] as [string, string]); const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIdsAndInstanceIds }); const { data: rulesBySlo, refetchRules } = useFetchRulesForSlo({ sloIds: sloIdsAndInstanceIds.map((item) => item[0]), @@ -45,7 +43,7 @@ export function SloListView({ sloList, loading, error }: Props) { return ( {sloList.map((slo) => ( - + historicalSummary.sloId === slo.id && - historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + historicalSummary.instanceId === slo.instanceId )?.data } historicalSummaryLoading={historicalSummaryLoading} diff --git a/x-pack/solutions/observability/plugins/slo/public/pages/slos/hooks/use_slo_summary.ts b/x-pack/solutions/observability/plugins/slo/public/pages/slos/hooks/use_slo_summary.ts index 0ad95a4408ab8..b640866190ebf 100644 --- a/x-pack/solutions/observability/plugins/slo/public/pages/slos/hooks/use_slo_summary.ts +++ b/x-pack/solutions/observability/plugins/slo/public/pages/slos/hooks/use_slo_summary.ts @@ -6,7 +6,7 @@ */ import numeral from '@elastic/numeral'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { IBasePath } from '@kbn/core-http-browser'; import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; import { useKibana } from '../../../hooks/use_kibana'; @@ -46,11 +46,7 @@ export const getSloFormattedSummary = ( : numeral(errorBudgetRemaining).format(percentFormat); const sloDetailsUrl = basePath.prepend( - paths.sloDetails( - slo.id, - ![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined, - slo.remote?.remoteName - ) + paths.sloDetails(slo.id, slo.instanceId, slo.remote?.remoteName) ); return { diff --git a/x-pack/solutions/observability/plugins/slo/public/utils/slo/remote_slo_urls.ts b/x-pack/solutions/observability/plugins/slo/public/utils/slo/remote_slo_urls.ts index f5d964173431a..30433a0c01ce6 100644 --- a/x-pack/solutions/observability/plugins/slo/public/utils/slo/remote_slo_urls.ts +++ b/x-pack/solutions/observability/plugins/slo/public/utils/slo/remote_slo_urls.ts @@ -6,7 +6,7 @@ */ import { encode } from '@kbn/rison'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import path from 'path'; import { paths } from '../../../common/locators/paths'; @@ -19,10 +19,7 @@ function createBaseRemoteSloDetailsUrl( } const spacePath = spaceId !== 'default' ? `/s/${spaceId}` : ''; - const detailsPath = paths.sloDetails( - slo.id, - ![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined - ); + const detailsPath = paths.sloDetails(slo.id, slo.instanceId); const remoteUrl = new URL(path.join(spacePath, detailsPath), slo.remote.kibanaUrl); return remoteUrl; diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_client.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_client.ts index 1bc1385a081b3..2406c56c809fe 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_client.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_client.ts @@ -8,7 +8,7 @@ import { SanitizedRule } from '@kbn/alerting-plugin/common'; import { RulesClient } from '@kbn/alerting-plugin/server'; import { SavedObject, SavedObjectsClientContract } from '@kbn/core/server'; import { termQuery } from '@kbn/observability-utils-server/es/queries/term_query'; -import { IStorageClient, StorageDocumentOf } from '@kbn/observability-utils-server/es/storage'; +import { IStorageClient } from '@kbn/observability-utils-server/es/storage'; import { keyBy } from 'lodash'; import objectHash from 'object-hash'; import pLimit from 'p-limit'; @@ -64,7 +64,7 @@ function getAssetDocument({ entityId, entityType, assetType, -}: AssetLink & { entityId: string; entityType: string }): StorageDocumentOf { +}: AssetLink & { entityId: string; entityType: string }) { const doc = { 'asset.id': assetId, 'asset.type': assetType, @@ -87,10 +87,17 @@ interface AssetBulkDeleteOperation { export type AssetBulkOperation = AssetBulkIndexOperation | AssetBulkDeleteOperation; +export interface StoredAssetLink { + 'asset.id': string; + 'asset.type': AssetType; + 'entity.id': string; + 'entity.type': string; +} + export class AssetClient { constructor( private readonly clients: { - storageClient: IStorageClient; + storageClient: IStorageClient; soClient: SavedObjectsClientContract; rulesClient: RulesClient; } diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_service.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_service.ts index 0a54e0abbd07a..47bd57433b9d8 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_service.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/assets/asset_service.ts @@ -8,8 +8,8 @@ import { CoreSetup, KibanaRequest, Logger } from '@kbn/core/server'; import { StorageIndexAdapter } from '@kbn/observability-utils-server/es/storage'; import { StreamsPluginStartDependencies } from '../../../types'; -import { AssetClient } from './asset_client'; -import { assetStorageSettings } from './storage_settings'; +import { AssetClient, StoredAssetLink } from './asset_client'; +import { AssetStorageSettings, assetStorageSettings } from './storage_settings'; export class AssetService { constructor( @@ -20,7 +20,7 @@ export class AssetService { async getClientWithRequest({ request }: { request: KibanaRequest }): Promise { const [coreStart, pluginsStart] = await this.coreSetup.getStartServices(); - const adapter = new StorageIndexAdapter( + const adapter = new StorageIndexAdapter( coreStart.elasticsearch.client.asInternalUser, this.logger.get('assets'), assetStorageSettings diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/client.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/client.ts index 0bfb31818632a..a0714e297679c 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/client.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/client.ts @@ -15,15 +15,19 @@ import type { IScopedClusterClient, Logger } from '@kbn/core/server'; import { isResponseError } from '@kbn/es-errors'; import { Condition, + GroupStreamDefinition, IngestStreamLifecycle, StreamDefinition, StreamUpsertRequest, UnwiredStreamDefinition, WiredStreamDefinition, + asIngestStreamDefinition, assertsSchema, getAncestors, getParentId, isChildOf, + isGroupStreamDefinition, + isIngestStreamDefinition, isDslLifecycle, isIlmLifecycle, isInheritLifecycle, @@ -34,6 +38,7 @@ import { } from '@kbn/streams-schema'; import { cloneDeep, keyBy, omit, orderBy } from 'lodash'; import { AssetClient } from './assets/asset_client'; +import { ForbiddenMemberTypeError } from './errors/forbidden_member_type_error'; import { syncUnwiredStreamDefinitionObjects, syncWiredStreamDefinitionObjects, @@ -321,6 +326,10 @@ export class StreamsClient { validateStreamTypeChanges(existingDefinition, definition); } + if (isGroupStreamDefinition(definition)) { + await this.assertValidGroupMembers({ definition }); + } + if (isRootStreamDefinition(definition)) { // only allow selective updates to a root stream validateRootStreamChanges( @@ -444,6 +453,29 @@ export class StreamsClient { return { parentDefinition }; } + /** + * Validates the members of the group streams to ensure they are NOT + * GroupStreamDefinitions + */ + async assertValidGroupMembers({ definition }: { definition: GroupStreamDefinition }) { + const { members } = definition.group; + + if (members.includes(definition.name)) { + throw new ForbiddenMemberTypeError('Group streams can not include themselves as a member'); + } + + await Promise.all( + members.map(async (name) => { + const memberStream = await this.getStream(name); + if (isGroupStreamDefinition(memberStream)) { + throw new ForbiddenMemberTypeError( + `Group streams can not be a member of a group, please remove [${name}]` + ); + } + }) + ); + } + /** * Forks a stream into a child with a specific condition. * It mostly defers to `upsertStream` for its validations, @@ -465,7 +497,7 @@ export class StreamsClient { name: string; if: Condition; }): Promise { - const parentDefinition = await this.getStream(parent); + const parentDefinition = asIngestStreamDefinition(await this.getStream(parent)); const childDefinition: WiredStreamDefinition = { name, @@ -526,30 +558,44 @@ export class StreamsClient { * Returns a stream definition for the given name: * - if a wired stream definition exists * - if an ingest stream definition exists - * - if a data stream exists (creates an ingest - * definition on the fly) + * - if a data stream exists (creates an ingest definition on the fly) + * - if a group stream definition exists * * Throws when: * - no definition is found * - the user does not have access to the stream */ async getStream(name: string): Promise { - const definition = await this.getStoredStreamDefinition(name) - .catch(async (error) => { + try { + const response = await this.dependencies.storageClient.get({ id: name }); + + const streamDefinition = response._source; + assertsSchema(streamDefinitionSchema, streamDefinition); + + if (isIngestStreamDefinition(streamDefinition)) { + const privileges = await checkAccess({ + id: name, + scopedClusterClient: this.dependencies.scopedClusterClient, + }); + if (!privileges.read) { + throw new DefinitionNotFoundError(`Stream definition for ${name} not found`); + } + } + return streamDefinition; + } catch (error) { + try { if (isElasticsearch404(error)) { const dataStream = await this.getDataStream(name); return this.getDataStreamAsIngestStream(dataStream); } throw error; - }) - .catch(async (error) => { - if (isElasticsearch404(error)) { + } catch (e) { + if (isElasticsearch404(e)) { throw new DefinitionNotFoundError(`Cannot find stream ${name}`); } - throw error; - }); - - return definition; + throw e; + } + } } private async getStoredStreamDefinition(name: string): Promise { @@ -678,11 +724,14 @@ export class StreamsClient { }); const privileges = await checkAccessBulk({ - ids: streams.map((stream) => stream.name), + ids: streams + .filter((stream) => !isGroupStreamDefinition(stream)) + .map((stream) => stream.name), scopedClusterClient, }); return streams.filter((stream) => { + if (isGroupStreamDefinition(stream)) return true; return privileges[stream.name]?.read === true; }); } @@ -695,13 +744,13 @@ export class StreamsClient { private async deleteStreamFromDefinition(definition: StreamDefinition): Promise { const { assetClient, logger, scopedClusterClient } = this.dependencies; - if (!isWiredStreamDefinition(definition)) { + if (isUnwiredStreamDefinition(definition)) { await deleteUnmanagedStreamObjects({ scopedClusterClient, id: definition.name, logger, }); - } else { + } else if (isWiredStreamDefinition(definition)) { const parentId = getParentId(definition.name); // need to update parent first to cut off documents streaming down @@ -763,15 +812,20 @@ export class StreamsClient { * Also verifies whether the user has access to the stream. */ async deleteStream(name: string): Promise { - const [definition, access] = await Promise.all([ - this.getStream(name).catch((error) => { - if (isDefinitionNotFoundError(error)) { - return undefined; - } - throw error; - }), - checkAccess({ id: name, scopedClusterClient: this.dependencies.scopedClusterClient }), - ]); + const definition = await this.getStream(name).catch((error) => { + if (isDefinitionNotFoundError(error)) { + return undefined; + } + throw error; + }); + + const access = + definition && isGroupStreamDefinition(definition) + ? { write: true, read: true } + : await checkAccess({ + id: name, + scopedClusterClient: this.dependencies.scopedClusterClient, + }); if (!access.write) { throw new SecurityError(`Cannot delete stream, insufficient privileges`); @@ -781,9 +835,11 @@ export class StreamsClient { return { acknowledged: true, result: 'noop' }; } - const parentId = getParentId(name); - if (isWiredStreamDefinition(definition) && !parentId) { - throw new MalformedStreamIdError('Cannot delete root stream'); + if (isWiredStreamDefinition(definition)) { + const parentId = getParentId(name); + if (!parentId) { + throw new MalformedStreamIdError('Cannot delete root stream'); + } } await this.deleteStreamFromDefinition(definition); @@ -794,13 +850,7 @@ export class StreamsClient { private async updateStoredStream(definition: StreamDefinition) { return this.dependencies.storageClient.index({ id: definition.name, - document: omit( - definition, - 'elasticsearch_assets', - 'dashboards', - 'inherited_fields', - 'lifecycle' - ), + document: definition, }); } diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/with_host_configured/index.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/errors/forbidden_member_type_error.ts similarity index 51% rename from x-pack/test/functional_enterprise_search/apps/enterprise_search/with_host_configured/index.ts rename to x-pack/solutions/observability/plugins/streams/server/lib/streams/errors/forbidden_member_type_error.ts index 9117515cc5667..e210de55f6c9b 100644 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/with_host_configured/index.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/errors/forbidden_member_type_error.ts @@ -4,11 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { StatusError } from './status_error'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('Enterprise Search', function () { - loadTestFile(require.resolve('./app_search/engines')); - }); +export class ForbiddenMemberTypeError extends StatusError { + constructor(message: string) { + super(message, 400); + this.name = 'ForbiddenMemberType'; + } } diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/helpers/validate_stream.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/helpers/validate_stream.ts index d9d8f11730126..0f142e4314b9c 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/helpers/validate_stream.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/helpers/validate_stream.ts @@ -9,6 +9,7 @@ import { StreamDefinition, WiredStreamDefinition, isIlmLifecycle, + isIngestStreamDefinition, isInheritLifecycle, isUnwiredStreamDefinition, isWiredStreamDefinition, @@ -97,6 +98,9 @@ export function validateStreamChildrenChanges( } export function validateStreamLifecycle(definition: StreamDefinition, isServerless: boolean) { + if (!isIngestStreamDefinition(definition)) { + return; + } const lifecycle = definition.ingest.lifecycle; if (isServerless && isIlmLifecycle(lifecycle)) { diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_ingest_pipeline.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_ingest_pipeline.ts index 18d35bc90e76b..da7a30aedd3e7 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_ingest_pipeline.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_ingest_pipeline.ts @@ -6,6 +6,7 @@ */ import { + IngestStreamDefinition, StreamDefinition, getParentId, isRoot, @@ -49,7 +50,7 @@ export function generateIngestPipeline( value: definition.name, }, }, - ...formatToIngestProcessors(definition.ingest.processing), + ...((isWiredStream && formatToIngestProcessors(definition.ingest.processing)) || []), { pipeline: { name: `${id}@stream.reroutes`, @@ -65,7 +66,7 @@ export function generateIngestPipeline( }; } -export function generateClassicIngestPipelineBody(definition: StreamDefinition) { +export function generateClassicIngestPipelineBody(definition: IngestStreamDefinition) { return { processors: formatToIngestProcessors(definition.ingest.processing), _meta: { diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_reroute_pipeline.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_reroute_pipeline.ts index 63b9ac21cada6..bd349272c6379 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_reroute_pipeline.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/ingest_pipelines/generate_reroute_pipeline.ts @@ -5,13 +5,13 @@ * 2.0. */ -import { StreamDefinition } from '@kbn/streams-schema'; +import { IngestStreamDefinition } from '@kbn/streams-schema'; import { ASSET_VERSION } from '../../../../common/constants'; import { conditionToPainless } from '../helpers/condition_to_painless'; import { getReroutePipelineName } from './name'; interface GenerateReroutePipelineParams { - definition: StreamDefinition; + definition: IngestStreamDefinition; } export function generateReroutePipeline({ definition }: GenerateReroutePipelineParams) { diff --git a/x-pack/solutions/observability/plugins/streams/server/lib/streams/service.ts b/x-pack/solutions/observability/plugins/streams/server/lib/streams/service.ts index 9208853e2c584..b79159d0cfaa3 100644 --- a/x-pack/solutions/observability/plugins/streams/server/lib/streams/service.ts +++ b/x-pack/solutions/observability/plugins/streams/server/lib/streams/service.ts @@ -12,6 +12,7 @@ import { StorageSettings, types, } from '@kbn/observability-utils-server/es/storage'; +import { StreamDefinition } from '@kbn/streams-schema'; import type { StreamsPluginStartDependencies } from '../../types'; import { StreamsClient } from './client'; import { AssetClient } from './assets/asset_client'; @@ -22,12 +23,13 @@ export const streamsStorageSettings = { properties: { name: types.keyword(), ingest: types.object({ enabled: false }), + group: types.object({ enabled: false }), }, }, } satisfies StorageSettings; export type StreamsStorageSettings = typeof streamsStorageSettings; -export type StreamsStorageClient = IStorageClient; +export type StreamsStorageClient = IStorageClient; export class StreamsService { constructor( @@ -50,7 +52,7 @@ export class StreamsService { const isServerless = coreStart.elasticsearch.getCapabilities().serverless; - const storageAdapter = new StorageIndexAdapter( + const storageAdapter = new StorageIndexAdapter( scopedClusterClient.asInternalUser, logger, streamsStorageSettings diff --git a/x-pack/solutions/observability/plugins/streams/server/plugin.ts b/x-pack/solutions/observability/plugins/streams/server/plugin.ts index ce4db83e6ea01..85fe61c86c93b 100644 --- a/x-pack/solutions/observability/plugins/streams/server/plugin.ts +++ b/x-pack/solutions/observability/plugins/streams/server/plugin.ts @@ -86,7 +86,12 @@ export class StreamsPlugin const scopedClusterClient = coreStart.elasticsearch.client.asScoped(request); const soClient = coreStart.savedObjects.getScopedClient(request); - return { scopedClusterClient, soClient, assetClient, streamsClient }; + return { + scopedClusterClient, + soClient, + assetClient, + streamsClient, + }; }, }, core, diff --git a/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/read_stream.ts b/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/read_stream.ts index 1189e04924c2e..b750252b49a44 100644 --- a/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/read_stream.ts +++ b/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/read_stream.ts @@ -6,9 +6,10 @@ */ import { - IngestStreamGetResponse, InheritedFieldDefinition, + StreamGetResponse, WiredStreamGetResponse, + isGroupStreamDefinition, isUnwiredStreamDefinition, } from '@kbn/streams-schema'; import { IScopedClusterClient } from '@kbn/core/server'; @@ -30,14 +31,25 @@ export async function readStream({ assetClient: AssetClient; streamsClient: StreamsClient; scopedClusterClient: IScopedClusterClient; -}): Promise { - const [streamDefinition, dashboards, ancestors, dataStream] = await Promise.all([ +}): Promise { + const [streamDefinition, dashboards] = await Promise.all([ streamsClient.getStream(name), assetClient.getAssetIds({ entityId: name, entityType: 'stream', assetType: 'dashboard', }), + ]); + + if (isGroupStreamDefinition(streamDefinition)) { + return { + stream: streamDefinition, + dashboards, + }; + } + + // These queries are only relavant for IngestStreams + const [ancestors, dataStream] = await Promise.all([ streamsClient.getAncestors(name), streamsClient.getDataStream(name).catch((e) => { if (e.statusCode === 404) { diff --git a/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/route.ts b/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/route.ts index ee2deb2c63115..c8a95e3f23206 100644 --- a/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/route.ts +++ b/x-pack/solutions/observability/plugins/streams/server/routes/streams/crud/route.ts @@ -7,6 +7,7 @@ import { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import { + isGroupStreamDefinition, StreamDefinition, StreamGetResponse, streamUpsertRequestSchema, @@ -76,9 +77,12 @@ export const streamDetailRoute = createServerRoute({ const { scopedClusterClient, streamsClient } = await getScopedClients({ request }); const streamEntity = await streamsClient.getStream(params.path.id); + const indexPattern = isGroupStreamDefinition(streamEntity) + ? streamEntity.group.members.join(',') + : streamEntity.name; // check doc count const docCountResponse = await scopedClusterClient.asCurrentUser.search({ - index: streamEntity.name, + index: indexPattern, body: { track_total_hits: true, query: { @@ -144,7 +148,6 @@ export const editStreamRoute = createServerRoute({ }), handler: async ({ params, request, getScopedClients }): Promise => { const { streamsClient } = await getScopedClients({ request }); - return await streamsClient.upsertStream({ request: params.body, name: params.path.id, diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/condition_editor/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/condition_editor/index.tsx index a095db7cdeb62..5948a5d3cf8d7 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/condition_editor/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/condition_editor/index.tsx @@ -28,25 +28,32 @@ import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/css'; import { CodeEditor } from '@kbn/code-editor'; -import { EMPTY_EQUALS_CONDITION } from '../../util/condition'; +import { + EMPTY_EQUALS_CONDITION, + alwaysToEmptyEquals, + emptyEqualsToAlways, +} from '../../util/condition'; export function ConditionEditor(props: { condition: Condition; readonly?: boolean; onConditionChange?: (condition: Condition) => void; }) { + const normalizedCondition = alwaysToEmptyEquals(props.condition); + + const handleConditionChange = (condition: Condition) => { + props.onConditionChange?.(emptyEqualsToAlways(condition)); + }; + if (props.readonly) { return ( - + ); } return ( - {})} - /> + ); } diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/management_bottom_bar/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/management_bottom_bar/index.tsx index 6855404cb3ffd..aa3b47106b6d6 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/management_bottom_bar/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/management_bottom_bar/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiBottomBar, EuiButton, EuiButtonEmpty, EuiFlexGroup } from '@elastic/eui'; +import { EuiButton, EuiButtonEmpty, EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useDiscardConfirm } from '../../hooks/use_discard_confirm'; @@ -25,37 +25,40 @@ export function ManagementBottomBar({ onCancel, onConfirm, }: ManagementBottomBarProps) { - const handleCancel = useDiscardConfirm(onCancel); + const handleCancel = useDiscardConfirm(onCancel, { + title: discardUnsavedChangesTitle, + message: discardUnsavedChangesMessage, + confirmButtonText: discardUnsavedChangesLabel, + cancelButtonText: keepEditingLabel, + }); return ( - - - - {i18n.translate('xpack.streams.streamDetailView.managementTab.bottomBar.cancel', { - defaultMessage: 'Cancel changes', - })} - - - - {confirmButtonText} - - - + + + {i18n.translate('xpack.streams.streamDetailView.managementTab.bottomBar.cancel', { + defaultMessage: 'Cancel changes', + })} + + + {confirmButtonText} + + ); } @@ -63,3 +66,26 @@ const defaultConfirmButtonText = i18n.translate( 'xpack.streams.streamDetailView.managementTab.bottomBar.confirm', { defaultMessage: 'Save changes' } ); + +const discardUnsavedChangesLabel = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.discardUnsavedChangesLabel', + { defaultMessage: 'Discard unsaved changes' } +); + +const keepEditingLabel = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.discardUnsavedChangesKeepEditing', + { defaultMessage: 'Keep editing' } +); + +const discardUnsavedChangesTitle = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.discardUnsavedChangesTitle', + { defaultMessage: 'Unsaved changes' } +); + +const discardUnsavedChangesMessage = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.discardUnsavedChangesMessage', + { + defaultMessage: + 'You are about to leave this view without saving. All changes will be lost. Do you really want to leave without saving?', + } +); diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/preview_table/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/preview_table/index.tsx index e98134c42935b..cd8fe4a80a391 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/preview_table/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/preview_table/index.tsx @@ -6,28 +6,18 @@ */ import { EuiDataGrid } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { CSSProperties, useEffect, useMemo, useState } from 'react'; +import { isEmpty } from 'lodash'; +import React, { useMemo } from 'react'; export function PreviewTable({ documents, displayColumns, - height, }: { documents: unknown[]; displayColumns?: string[]; - height?: CSSProperties['height']; }) { - const [computedHeight, setComputedHeight] = useState('100px'); - useEffect(() => { - // set height to 100% after a short delay otherwise it doesn't calculate correctly - // TODO: figure out a better way to do this - setTimeout(() => { - setComputedHeight(`100%`); - }, 50); - }, []); - const columns = useMemo(() => { - if (displayColumns) return displayColumns; + if (displayColumns && !isEmpty(displayColumns)) return displayColumns; const cols = new Set(); documents.forEach((doc) => { @@ -42,9 +32,10 @@ export function PreviewTable({ }, [displayColumns, documents]); const gridColumns = useMemo(() => { - return Array.from(columns).map((column) => ({ + return columns.map((column) => ({ id: column, displayAsText: column, + initialWidth: columns.length > 10 ? 250 : undefined, })); }, [columns]); @@ -61,7 +52,6 @@ export function PreviewTable({ }} toolbarVisibility={false} rowCount={documents.length} - height={height ?? computedHeight} renderCellValue={({ rowIndex, columnId }) => { const doc = documents[rowIndex]; if (!doc || typeof doc !== 'object') { diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/add_processor_button.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/add_processor_button.tsx deleted file mode 100644 index d671b107cc239..0000000000000 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/add_processor_button.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiButton } from '@elastic/eui'; -import { EuiButtonPropsForButton } from '@elastic/eui/src/components/button/button'; -import { i18n } from '@kbn/i18n'; - -export function AddProcessorButton(props: EuiButtonPropsForButton) { - return ( - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichmentEmptyPrompt.addProcessorAction', - { defaultMessage: 'Add a processor' } - )} - - ); -} diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/enrichment_empty_prompt.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/enrichment_empty_prompt.tsx deleted file mode 100644 index 96c8bddb2c822..0000000000000 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/enrichment_empty_prompt.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { EuiEmptyPrompt } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { AssetImage } from '../asset_image'; -import { AddProcessorButton } from './add_processor_button'; - -interface EnrichmentEmptyPromptProps { - onAddProcessor: () => void; -} - -export const EnrichmentEmptyPrompt = ({ onAddProcessor }: EnrichmentEmptyPromptProps) => { - return ( - } - title={title} - body={body} - actions={[]} - /> - ); -}; - -const title = ( -

- {i18n.translate('xpack.streams.streamDetailView.managementTab.enrichmentEmptyPrompt.title', { - defaultMessage: 'Start extracting useful fields from your data', - })} -

-); - -const body = ( -

- {i18n.translate('xpack.streams.streamDetailView.managementTab.enrichmentEmptyPrompt.body', { - defaultMessage: 'Use processors to transform data before indexing', - })} -

-); diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/danger_zone.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/danger_zone.tsx deleted file mode 100644 index 3bec4c48d2912..0000000000000 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/danger_zone.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { - EuiPanel, - EuiTitle, - EuiSpacer, - useGeneratedHtmlId, - EuiButton, - EuiConfirmModal, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { useBoolean } from '@kbn/react-hooks'; - -export const DangerZone = ({ - onDeleteProcessor, -}: Pick) => { - return ( - - -

- {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.dangerAreaTitle', - { defaultMessage: 'Danger area' } - )} -

-
- - -
- ); -}; - -interface DeleteProcessorButtonProps { - onDeleteProcessor: () => void; -} - -const DeleteProcessorButton = ({ onDeleteProcessor }: DeleteProcessorButtonProps) => { - const [isConfirmModalOpen, { on: openConfirmModal, off: closeConfirmModal }] = useBoolean(); - const confirmModalId = useGeneratedHtmlId(); - - return ( - <> - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.dangerAreaTitle', - { defaultMessage: 'Delete processor' } - )} - - {isConfirmModalOpen && ( - -

- {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.deleteProcessorModalBody', - { - defaultMessage: - 'You can still reset this until the changes are confirmed on the processors list.', - } - )} -

-
- )} - - ); -}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/index.tsx deleted file mode 100644 index 0e3452d22cdaa..0000000000000 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/index.tsx +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useMemo } from 'react'; -import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { EuiCallOut, EuiForm, EuiButton, EuiSpacer, EuiHorizontalRule } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { ReadStreamDefinition } from '@kbn/streams-schema'; -import { isEqual } from 'lodash'; -import { dynamic } from '@kbn/shared-ux-utility'; -import { ProcessorTypeSelector } from './processor_type_selector'; -import { ProcessorFlyoutTemplate } from './processor_flyout_template'; -import { - DetectedField, - EnrichmentUIProcessorDefinition, - ProcessingDefinition, - ProcessorFormState, -} from '../types'; -import { DangerZone } from './danger_zone'; -import { DissectProcessorForm } from './dissect'; -import { GrokProcessorForm } from './grok'; -import { convertFormStateToProcessing, getDefaultFormState } from '../utils'; -import { useProcessingSimulator } from '../hooks/use_processing_simulator'; - -const ProcessorOutcomePreview = dynamic(() => - import(/* webpackChunkName: "management_processor_outcome" */ './processor_outcome_preview').then( - (mod) => ({ - default: mod.ProcessorOutcomePreview, - }) - ) -); - -export interface ProcessorFlyoutProps { - definition: ReadStreamDefinition; - onClose: () => void; -} - -export interface AddProcessorFlyoutProps extends ProcessorFlyoutProps { - onAddProcessor: (newProcessing: ProcessingDefinition, newFields?: DetectedField[]) => void; -} -export interface EditProcessorFlyoutProps extends ProcessorFlyoutProps { - processor: EnrichmentUIProcessorDefinition; - onDeleteProcessor: (id: string) => void; - onUpdateProcessor: (id: string, processor: EnrichmentUIProcessorDefinition) => void; -} - -export function AddProcessorFlyout({ - definition, - onAddProcessor, - onClose, -}: AddProcessorFlyoutProps) { - const defaultValues = useMemo(() => getDefaultFormState('grok'), []); - - const methods = useForm({ defaultValues, mode: 'onChange' }); - - const formFields = methods.watch(); - - const hasChanges = useMemo( - () => !isEqual(defaultValues, formFields), - [defaultValues, formFields] - ); - - const { error, isLoading, refreshSamples, simulation, samples, simulate } = - useProcessingSimulator({ - definition, - condition: { field: formFields.field, operator: 'exists' }, - }); - - const handleSubmit: SubmitHandler = async (data) => { - const processingDefinition = convertFormStateToProcessing(data); - - simulate(processingDefinition, data.detected_fields).then((responseBody) => { - if (responseBody instanceof Error) return; - - onAddProcessor(processingDefinition, data.detected_fields); - onClose(); - }); - }; - - return ( - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.confirmAddProcessor', - { defaultMessage: 'Add processor' } - )} - - } - > - - - - - {formFields.type === 'grok' && } - {formFields.type === 'dissect' && } - - - - - - ); -} - -export function EditProcessorFlyout({ - onClose, - onDeleteProcessor, - onUpdateProcessor, - processor, -}: EditProcessorFlyoutProps) { - const processorType = 'grok' in processor.config ? 'grok' : 'dissect'; - - const defaultValues = useMemo( - () => getDefaultFormState(processorType, processor), - [processor, processorType] - ); - - const methods = useForm({ defaultValues, mode: 'onChange' }); - - const formFields = methods.watch(); - - const hasChanges = useMemo( - () => !isEqual(defaultValues, formFields), - [defaultValues, formFields] - ); - - const handleSubmit: SubmitHandler = (data) => { - const processingDefinition = convertFormStateToProcessing(data); - - onUpdateProcessor(processor.id, { id: processor.id, ...processingDefinition }); - onClose(); - }; - - const handleProcessorDelete = () => { - onDeleteProcessor(processor.id); - onClose(); - }; - - return ( - - } - confirmButton={ - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.confirmEditProcessor', - { defaultMessage: 'Update processor' } - )} - - } - > - - - - - {formFields.type === 'grok' && } - {formFields.type === 'dissect' && } - - - - - - ); -} diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_flyout_template.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_flyout_template.tsx deleted file mode 100644 index 8f9de6117b4ae..0000000000000 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_flyout_template.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { PropsWithChildren } from 'react'; -import { - EuiFlyoutResizable, - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlexGroup, - EuiButtonEmpty, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { useDiscardConfirm } from '../../../hooks/use_discard_confirm'; - -interface ProcessorFlyoutTemplateProps { - banner?: React.ReactNode; - confirmButton?: React.ReactNode; - onClose: () => void; - shouldConfirm?: boolean; - title: string; -} - -export function ProcessorFlyoutTemplate({ - banner, - children, - confirmButton, - onClose, - shouldConfirm = false, - title, -}: PropsWithChildren) { - const handleClose = useDiscardConfirm(onClose); - - const closeHandler = shouldConfirm ? handleClose : onClose; - - return ( - - - -

{title}

-
-
- {children} - - - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.cancel', - { defaultMessage: 'Cancel' } - )} - - {confirmButton} - - -
- ); -} diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_outcome_preview.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_outcome_preview.tsx deleted file mode 100644 index 9bf657d654daa..0000000000000 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_outcome_preview.tsx +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect, useMemo, useState } from 'react'; -import { useDateRange } from '@kbn/observability-utils-browser/hooks/use_date_range'; -import { - EuiPanel, - EuiTitle, - EuiSpacer, - EuiFlexGroup, - EuiFilterButton, - EuiFilterGroup, - EuiEmptyPrompt, - EuiLoadingLogo, - EuiButton, - EuiFormRow, - EuiSuperSelectOption, - EuiSuperSelect, - useEuiTheme, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { TimeRange } from '@kbn/es-query'; -import { isEmpty } from 'lodash'; -import { FieldIcon } from '@kbn/react-field'; -import { - FIELD_DEFINITION_TYPES, - ReadStreamDefinition, - isWiredReadStream, -} from '@kbn/streams-schema'; -import { UseControllerProps, useController, useFieldArray } from 'react-hook-form'; -import { css } from '@emotion/react'; -import { flattenObject } from '@kbn/object-utils'; -import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; -import { useKibana } from '../../../hooks/use_kibana'; -import { StreamsAppSearchBar, StreamsAppSearchBarProps } from '../../streams_app_search_bar'; -import { PreviewTable } from '../../preview_table'; -import { convertFormStateToProcessing } from '../utils'; -import { DetectedField, ProcessorFormState } from '../types'; -import { UseProcessingSimulatorReturnType } from '../hooks/use_processing_simulator'; - -interface ProcessorOutcomePreviewProps { - definition: ReadStreamDefinition; - formFields: ProcessorFormState; - isLoading: UseProcessingSimulatorReturnType['isLoading']; - simulation: UseProcessingSimulatorReturnType['simulation']; - samples: UseProcessingSimulatorReturnType['samples']; - onRefreshSamples: UseProcessingSimulatorReturnType['refreshSamples']; - onSimulate: UseProcessingSimulatorReturnType['simulate']; - simulationError: UseProcessingSimulatorReturnType['error']; -} - -export const ProcessorOutcomePreview = ({ - definition, - formFields, - isLoading, - simulation, - samples, - onRefreshSamples, - onSimulate, - simulationError, -}: ProcessorOutcomePreviewProps) => { - const { dependencies } = useKibana(); - const { data } = dependencies.start; - - const { timeRange, setTimeRange } = useDateRange({ data }); - - const [selectedDocsFilter, setSelectedDocsFilter] = - useState('outcome_filter_all'); - - const simulationDocuments = useMemo(() => { - if (!simulation?.documents) { - return samples.map((doc) => flattenObject(doc)); - } - - const filterDocuments = (filter: DocsFilterOption) => { - switch (filter) { - case 'outcome_filter_matched': - return simulation.documents.filter((doc) => doc.isMatch); - case 'outcome_filter_unmatched': - return simulation.documents.filter((doc) => !doc.isMatch); - case 'outcome_filter_all': - default: - return simulation.documents; - } - }; - - return filterDocuments(selectedDocsFilter).map((doc) => doc.value); - }, [samples, simulation?.documents, selectedDocsFilter]); - - const detectedFieldsColumns = useMemo( - () => - simulation?.detected_fields ? simulation.detected_fields.map((field) => field.name) : [], - [simulation?.detected_fields] - ); - - const tableColumns = useMemo(() => { - switch (selectedDocsFilter) { - case 'outcome_filter_unmatched': - return [formFields.field]; - case 'outcome_filter_matched': - case 'outcome_filter_all': - default: - return [formFields.field, ...detectedFieldsColumns]; - } - }, [formFields.field, detectedFieldsColumns, selectedDocsFilter]); - - const detectedFieldsEnabled = - isWiredReadStream(definition) && - ((simulation && !isEmpty(simulation.detected_fields)) || !isEmpty(formFields.detected_fields)); - - return ( - - - -

- {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomeTitle', - { defaultMessage: 'Outcome' } - )} -

-
- { - onSimulate(convertFormStateToProcessing(formFields), formFields.detected_fields); - }} - isLoading={isLoading} - > - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.runSimulation', - { defaultMessage: 'Run simulation' } - )} - -
- - {detectedFieldsEnabled && } - - - -
- ); -}; - -const docsFilterOptions = { - outcome_filter_all: { - id: 'outcome_filter_all', - label: i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomeControls.all', - { defaultMessage: 'All samples' } - ), - }, - outcome_filter_matched: { - id: 'outcome_filter_matched', - label: i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomeControls.matched', - { defaultMessage: 'Matched' } - ), - }, - outcome_filter_unmatched: { - id: 'outcome_filter_unmatched', - label: i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomeControls.unmatched', - { defaultMessage: 'Unmatched' } - ), - }, -} as const; - -type DocsFilterOption = keyof typeof docsFilterOptions; - -interface OutcomeControlsProps { - docsFilter: DocsFilterOption; - timeRange: TimeRange; - onDocsFilterChange: (filter: DocsFilterOption) => void; - onTimeRangeChange: (timeRange: TimeRange) => void; - onTimeRangeRefresh: () => void; - simulationFailureRate?: number; - simulationSuccessRate?: number; -} - -const OutcomeControls = ({ - docsFilter, - timeRange, - onDocsFilterChange, - onTimeRangeChange, - onTimeRangeRefresh, - simulationFailureRate, - simulationSuccessRate, -}: OutcomeControlsProps) => { - const handleQuerySubmit: StreamsAppSearchBarProps['onQuerySubmit'] = ( - { dateRange }, - isUpdate - ) => { - if (!isUpdate) { - return onTimeRangeRefresh(); - } - - if (dateRange) { - onTimeRangeChange({ - from: dateRange.from, - to: dateRange?.to, - mode: dateRange.mode, - }); - } - }; - - return ( - - - onDocsFilterChange(docsFilterOptions.outcome_filter_all.id)} - > - {docsFilterOptions.outcome_filter_all.label} - - onDocsFilterChange(docsFilterOptions.outcome_filter_matched.id)} - badgeColor="success" - numActiveFilters={ - simulationSuccessRate ? parseFloat((simulationSuccessRate * 100).toFixed(2)) : undefined - } - > - {docsFilterOptions.outcome_filter_matched.label} - - onDocsFilterChange(docsFilterOptions.outcome_filter_unmatched.id)} - badgeColor="accent" - numActiveFilters={ - simulationFailureRate ? parseFloat((simulationFailureRate * 100).toFixed(2)) : undefined - } - > - {docsFilterOptions.outcome_filter_unmatched.label} - - - - - ); -}; - -const DetectedFields = ({ detectedFields }: { detectedFields?: DetectedField[] }) => { - const { euiTheme } = useEuiTheme(); - const { fields, replace } = useFieldArray<{ detected_fields: DetectedField[] }>({ - name: 'detected_fields', - }); - - useEffect(() => { - if (detectedFields) replace(detectedFields); - }, [detectedFields, replace]); - - return ( - - - {fields.map((field, id) => ( - - ))} - - - ); -}; - -const DetectedFieldSelector = ( - props: UseControllerProps -) => { - const { field } = useController(props); - - const options = useMemo(() => getDetectedFieldSelectOptions(field.value), [field.value]); - - return ( - field.onChange({ ...field.value, type })} - css={css` - min-inline-size: 180px; - `} - /> - ); -}; - -const getDetectedFieldSelectOptions = ( - fieldValue: DetectedField -): Array> => - [...FIELD_DEFINITION_TYPES, 'unmapped'].map((type) => ({ - value: type, - inputDisplay: ( - - - {fieldValue.name} - - ), - dropdownDisplay: ( - - - {type} - - ), - })); - -interface OutcomePreviewTableProps { - documents?: Array>; - columns: string[]; - error?: IHttpFetchError; - isLoading?: boolean; -} - -const OutcomePreviewTable = ({ - documents = [], - columns, - error, - isLoading, -}: OutcomePreviewTableProps) => { - if (error) { - return ( - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomePreviewTable.errorTitle', - { defaultMessage: 'Unable to display the simulation outcome for this processor.' } - )} - - } - body={ - <> -

- {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomePreviewTable.errorBody', - { defaultMessage: 'The processor did not run correctly.' } - )} -

- {error.body?.message ?

{error.body.message}

: null} - - } - /> - ); - } - - if (isLoading) { - return ( - } - title={ -

- {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomePreviewTable.loadingTitle', - { defaultMessage: 'Running processor simulation' } - )} -

- } - /> - ); - } - - if (documents?.length === 0) { - return ( - - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.outcomePreviewTable.noDataTitle', - { - defaultMessage: - 'There are no simulation outcome documents for the current selection.', - } - )} -

- } - /> - ); - } - - return ; -}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_definition.ts b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_definition.ts index b9a03a71958a2..8199df9beffb1 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_definition.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_definition.ts @@ -5,26 +5,44 @@ * 2.0. */ -import { useState, useMemo, useEffect } from 'react'; +import { useState, useMemo, useEffect, useRef, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { useAbortController } from '@kbn/observability-utils-browser/hooks/use_abort_controller'; import { useBoolean } from '@kbn/react-hooks'; import { - ReadStreamDefinition, - isWiredReadStream, + IngestStreamGetResponse, + isWiredStreamGetResponse, FieldDefinition, - WiredReadStreamDefinition, - ProcessorDefinition, - getProcessorConfig, + WiredStreamGetResponse, IngestUpsertRequest, + ProcessorDefinition, + getProcessorType, } from '@kbn/streams-schema'; -import { htmlIdGenerator } from '@elastic/eui'; -import { isEqual, omit } from 'lodash'; -import { DetectedField, EnrichmentUIProcessorDefinition, ProcessingDefinition } from '../types'; +import { DetectedField, ProcessorDefinitionWithUIAttributes } from '../types'; import { useKibana } from '../../../hooks/use_kibana'; -import { alwaysToEmptyEquals, emptyEqualsToAlways } from '../../../util/condition'; - -export const useDefinition = (definition: ReadStreamDefinition, refreshDefinition: () => void) => { +import { processorConverter } from '../utils'; + +export interface UseDefinitionReturn { + processors: ProcessorDefinitionWithUIAttributes[]; + hasChanges: boolean; + isSavingChanges: boolean; + addProcessor: (newProcessor: ProcessorDefinition, newFields?: DetectedField[]) => void; + updateProcessor: ( + id: string, + processor: ProcessorDefinition, + status?: ProcessorDefinitionWithUIAttributes['status'] + ) => void; + deleteProcessor: (id: string) => void; + reorderProcessors: (processors: ProcessorDefinitionWithUIAttributes[]) => void; + saveChanges: () => Promise; + setProcessors: (processors: ProcessorDefinitionWithUIAttributes[]) => void; + resetChanges: () => void; +} + +export const useDefinition = ( + definition: IngestStreamGetResponse, + refreshDefinition: () => void +): UseDefinitionReturn => { const { core, dependencies } = useKibana(); const { toasts } = core.notifications; @@ -37,46 +55,79 @@ export const useDefinition = (definition: ReadStreamDefinition, refreshDefinitio const [processors, setProcessors] = useState(() => createProcessorsList(existingProcessorDefinitions) ); + + const initialProcessors = useRef(processors); + const [fields, setFields] = useState(() => - isWiredReadStream(definition) ? definition.stream.ingest.wired.fields : {} + isWiredStreamGetResponse(definition) ? definition.stream.ingest.wired.fields : {} ); const nextProcessorDefinitions = useMemo( - () => processors.map(convertUiDefinitionIntoApiDefinition), + () => processors.map(processorConverter.toAPIDefinition), [processors] ); useEffect(() => { // Reset processors when definition refreshes - setProcessors(createProcessorsList(definition.stream.ingest.processing)); + const resetProcessors = createProcessorsList(definition.stream.ingest.processing); + setProcessors(resetProcessors); + initialProcessors.current = resetProcessors; }, [definition]); const hasChanges = useMemo( - () => !isEqual(existingProcessorDefinitions, nextProcessorDefinitions), - [existingProcessorDefinitions, nextProcessorDefinitions] + () => + processors.length !== initialProcessors.current.length || // Processor count changed, a processor might be deleted + processors.some((proc) => proc.status === 'draft' || proc.status === 'updated') || // New or updated processors + hasOrderChanged(processors, initialProcessors.current), // Processor order changed + [processors] ); - const addProcessor = (newProcessing: ProcessingDefinition, newFields?: DetectedField[]) => { - setProcessors((prevProcs) => prevProcs.concat({ ...newProcessing, id: createId() })); + const addProcessor = useCallback( + (newProcessor: ProcessorDefinition, newFields?: DetectedField[]) => { + setProcessors((prevProcs) => + prevProcs.concat(processorConverter.toUIDefinition(newProcessor, { status: 'draft' })) + ); - if (isWiredReadStream(definition) && newFields) { - setFields((currentFields) => mergeFields(definition, currentFields, newFields)); - } - }; + if (isWiredStreamGetResponse(definition) && newFields) { + setFields((currentFields) => mergeFields(definition, currentFields, newFields)); + } + }, + [definition] + ); - const updateProcessor = (id: string, processorUpdate: EnrichmentUIProcessorDefinition) => { - setProcessors((prevProcs) => - prevProcs.map((proc) => (proc.id === id ? processorUpdate : proc)) - ); - }; + const updateProcessor = useCallback( + ( + id: string, + processorUpdate: ProcessorDefinition, + status: ProcessorDefinitionWithUIAttributes['status'] = 'updated' + ) => { + setProcessors((prevProcs) => + prevProcs.map((proc) => + proc.id === id + ? { + ...processorUpdate, + id, + type: getProcessorType(processorUpdate), + status, + } + : proc + ) + ); + }, + [] + ); - const deleteProcessor = (id: string) => { + const reorderProcessors = setProcessors; + + const deleteProcessor = useCallback((id: string) => { setProcessors((prevProcs) => prevProcs.filter((proc) => proc.id !== id)); - }; + }, []); const resetChanges = () => { - setProcessors(createProcessorsList(existingProcessorDefinitions)); - setFields(isWiredReadStream(definition) ? definition.stream.ingest.wired.fields : {}); + const resetProcessors = createProcessorsList(existingProcessorDefinitions); + setProcessors(resetProcessors); + initialProcessors.current = resetProcessors; + setFields(isWiredStreamGetResponse(definition) ? definition.stream.ingest.wired.fields : {}); }; const saveChanges = async () => { @@ -86,13 +137,13 @@ export const useDefinition = (definition: ReadStreamDefinition, refreshDefinitio signal: abortController.signal, params: { path: { - id: definition.name, + id: definition.stream.name, }, body: { ingest: { ...definition.stream.ingest, processing: nextProcessorDefinitions, - ...(isWiredReadStream(definition) && { wired: { fields } }), + ...(isWiredStreamGetResponse(definition) && { wired: { fields } }), }, } as IngestUpsertRequest, }, @@ -104,8 +155,10 @@ export const useDefinition = (definition: ReadStreamDefinition, refreshDefinitio { defaultMessage: "Stream's processors updated" } ) ); + + refreshDefinition(); } catch (error) { - toasts.addError(error, { + toasts.addError(new Error(error.body.message), { title: i18n.translate( 'xpack.streams.streamDetailView.managementTab.enrichment.saveChangesError', { defaultMessage: "An issue occurred saving processors' changes." } @@ -113,7 +166,6 @@ export const useDefinition = (definition: ReadStreamDefinition, refreshDefinitio toastMessage: error.body.message, }); } finally { - await refreshDefinition(); endsSaving(); } }; @@ -125,6 +177,7 @@ export const useDefinition = (definition: ReadStreamDefinition, refreshDefinitio addProcessor, updateProcessor, deleteProcessor, + reorderProcessors, resetChanges, saveChanges, setProcessors, @@ -134,46 +187,19 @@ export const useDefinition = (definition: ReadStreamDefinition, refreshDefinitio }; }; -const createId = htmlIdGenerator(); -const createProcessorsList = ( - processors: ProcessorDefinition[] -): EnrichmentUIProcessorDefinition[] => processors.map(createProcessorWithId); - -const createProcessorWithId = ( - processor: ProcessorDefinition -): EnrichmentUIProcessorDefinition => ({ - condition: alwaysToEmptyEquals(getProcessorConfig(processor).if), - config: { - ...('grok' in processor - ? { grok: omit(processor.grok, 'if') } - : { dissect: omit(processor.dissect, 'if') }), - }, - id: createId(), -}); - -const convertUiDefinitionIntoApiDefinition = ( - processor: EnrichmentUIProcessorDefinition -): ProcessorDefinition => { - const { id: _id, config, condition } = processor; - - if ('grok' in config) { - return { - grok: { - ...config.grok, - if: emptyEqualsToAlways(condition), - }, - }; - } - return { - dissect: { - ...config.dissect, - if: emptyEqualsToAlways(condition), - }, - }; +const createProcessorsList = (processors: ProcessorDefinition[]) => { + return processors.map((processor) => processorConverter.toUIDefinition(processor)); +}; + +const hasOrderChanged = ( + processors: ProcessorDefinitionWithUIAttributes[], + initialProcessors: ProcessorDefinitionWithUIAttributes[] +) => { + return processors.some((processor, index) => processor.id !== initialProcessors[index].id); }; const mergeFields = ( - definition: WiredReadStreamDefinition, + definition: WiredStreamGetResponse, currentFields: FieldDefinition, newFields: DetectedField[] ) => { diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_processing_simulator.ts b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_processing_simulator.ts index 18abbbfd5b217..bc3bd4755d709 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_processing_simulator.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/hooks/use_processing_simulator.ts @@ -5,49 +5,53 @@ * 2.0. */ -/* eslint-disable @typescript-eslint/naming-convention */ - -import { useAbortController } from '@kbn/observability-utils-browser/hooks/use_abort_controller'; +import { useEffect, useMemo, useState } from 'react'; +import { debounce, isEmpty, uniq, uniqBy } from 'lodash'; import { - DissectProcessorDefinition, - ReadStreamDefinition, + IngestStreamGetResponse, + getProcessorConfig, + UnaryOperator, Condition, - ProcessorDefinition, - GrokProcessorDefinition, + processorDefinitionSchema, + isSchema, } from '@kbn/streams-schema'; -import useAsyncFn from 'react-use/lib/useAsyncFn'; import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; import { useDateRange } from '@kbn/observability-utils-browser/hooks/use_date_range'; -import { APIReturnType, StreamsAPIClientRequestParamsOf } from '@kbn/streams-plugin/public/api'; +import { APIReturnType } from '@kbn/streams-plugin/public/api'; import { useStreamsAppFetch } from '../../../hooks/use_streams_app_fetch'; import { useKibana } from '../../../hooks/use_kibana'; -import { ProcessingDefinition } from '../types'; -import { DetectedField } from '../types'; -import { emptyEqualsToAlways } from '../../../util/condition'; +import { DetectedField, ProcessorDefinitionWithUIAttributes } from '../types'; +import { processorConverter } from '../utils'; type Simulation = APIReturnType<'POST /api/streams/{id}/processing/_simulate'>; -type SimulationRequestBody = - StreamsAPIClientRequestParamsOf<'POST /api/streams/{id}/processing/_simulate'>['params']['body']; -export interface UseProcessingSimulatorReturnType { +export interface TableColumn { + name: string; + origin: 'processor' | 'detected'; +} + +export interface UseProcessingSimulatorProps { + definition: IngestStreamGetResponse; + processors: ProcessorDefinitionWithUIAttributes[]; +} + +export interface UseProcessingSimulatorReturn { + hasLiveChanges: boolean; error?: IHttpFetchError; isLoading: boolean; - refreshSamples: () => void; samples: Array>; - simulate: ( - processing: ProcessingDefinition, - detectedFields?: DetectedField[] - ) => Promise; simulation?: Simulation | null; + tableColumns: TableColumn[]; + refreshSamples: () => void; + watchProcessor: ( + processor: ProcessorDefinitionWithUIAttributes | { id: string; deleteIfExists: true } + ) => void; } export const useProcessingSimulator = ({ definition, - condition, -}: { - definition: ReadStreamDefinition; - condition?: Condition; -}): UseProcessingSimulatorReturnType => { + processors, +}: UseProcessingSimulatorProps): UseProcessingSimulatorReturn => { const { dependencies } = useKibana(); const { data, @@ -58,9 +62,57 @@ export const useProcessingSimulator = ({ absoluteTimeRange: { start, end }, } = useDateRange({ data }); - const abortController = useAbortController(); + const draftProcessors = useMemo( + () => processors.filter((processor) => processor.status === 'draft'), + [processors] + ); - const serializedCondition = JSON.stringify(condition); + const [liveDraftProcessors, setLiveDraftProcessors] = useState(draftProcessors); + + useEffect(() => { + setLiveDraftProcessors((prevLiveProcessors) => { + const inProgressDraft = prevLiveProcessors.find((proc) => proc.id === 'draft'); + return inProgressDraft ? [...draftProcessors, inProgressDraft] : draftProcessors; + }); + }, [draftProcessors]); + + const watchProcessor = useMemo( + () => + debounce( + (processor: ProcessorDefinitionWithUIAttributes | { id: string; deleteIfExists: true }) => { + if ('deleteIfExists' in processor) { + return setLiveDraftProcessors((prevLiveDraftProcessors) => + prevLiveDraftProcessors.filter((proc) => proc.id !== processor.id) + ); + } + + if (processor.status === 'draft') { + setLiveDraftProcessors((prevLiveDraftProcessors) => { + const newLiveDraftProcessors = prevLiveDraftProcessors.slice(); + + const existingIndex = prevLiveDraftProcessors.findIndex( + (proc) => proc.id === processor.id + ); + + if (existingIndex !== -1) { + newLiveDraftProcessors[existingIndex] = processor; + } else { + newLiveDraftProcessors.push(processor); + } + + return newLiveDraftProcessors; + }); + } + }, + 500 + ), + [] + ); + + const samplingCondition = useMemo( + () => composeSamplingCondition(liveDraftProcessors), + [liveDraftProcessors] + ); const { loading: isLoadingSamples, @@ -75,9 +127,9 @@ export const useProcessingSimulator = ({ return streamsRepositoryClient.fetch('POST /api/streams/{id}/_sample', { signal, params: { - path: { id: definition.name }, + path: { id: definition.stream.name }, body: { - if: condition ? emptyEqualsToAlways(condition) : { always: {} }, + if: samplingCondition, start: start?.valueOf(), end: end?.valueOf(), size: 100, @@ -85,69 +137,103 @@ export const useProcessingSimulator = ({ }, }); }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [definition, streamsRepositoryClient, start, end, serializedCondition], + [definition, streamsRepositoryClient, start, end, samplingCondition], { disableToastOnError: true } ); - const sampleDocs = (samples?.documents ?? []) as Array>; + const sampleDocs = samples?.documents as Array>; - const [{ loading: isLoadingSimulation, error, value }, simulate] = useAsyncFn( - (processingDefinition: ProcessingDefinition, detectedFields?: DetectedField[]) => { - if (!definition) { + const { + loading: isLoadingSimulation, + value: simulation, + error: simulationError, + } = useStreamsAppFetch( + ({ signal }) => { + if (!definition || isEmpty(sampleDocs) || isEmpty(liveDraftProcessors)) { return Promise.resolve(null); } - const processorDefinition: ProcessorDefinition = - 'grok' in processingDefinition.config - ? ({ - grok: { - field: processingDefinition.config.grok.field, - ignore_failure: processingDefinition.config.grok.ignore_failure, - ignore_missing: processingDefinition.config.grok.ignore_missing, - if: emptyEqualsToAlways(processingDefinition.condition), - patterns: processingDefinition.config.grok.patterns, - pattern_definitions: processingDefinition.config.grok.pattern_definitions, - }, - } satisfies GrokProcessorDefinition) - : ({ - dissect: { - field: processingDefinition.config.dissect.field, - ignore_failure: processingDefinition.config.dissect.ignore_failure, - ignore_missing: processingDefinition.config.dissect.ignore_missing, - if: emptyEqualsToAlways(processingDefinition.condition), - pattern: processingDefinition.config.dissect.pattern, - append_separator: processingDefinition.config.dissect.append_separator, - }, - } satisfies DissectProcessorDefinition); - - const detected_fields = detectedFields - ? (detectedFields.filter( - (field) => field.type !== 'unmapped' - ) as SimulationRequestBody['detected_fields']) - : undefined; + const processing = liveDraftProcessors.map(processorConverter.toAPIDefinition); + + const hasValidProcessors = processing.every((processor) => + isSchema(processorDefinitionSchema, processor) + ); + + // Each processor should meet the minimum schema requirements to run the simulation + if (!hasValidProcessors) { + return Promise.resolve(null); + } return streamsRepositoryClient.fetch('POST /api/streams/{id}/processing/_simulate', { - signal: abortController.signal, + signal, params: { - path: { id: definition.name }, + path: { id: definition.stream.name }, body: { documents: sampleDocs, - processing: [processorDefinition], - detected_fields, + processing: liveDraftProcessors.map(processorConverter.toAPIDefinition), }, }, }); }, - [definition, sampleDocs] + [definition, sampleDocs, liveDraftProcessors, streamsRepositoryClient], + { disableToastOnError: true } ); + const tableColumns = useMemo(() => { + // If there is an error, we only want the source fields + const detectedFields = simulationError ? [] : simulation?.detected_fields ?? []; + + return getTableColumns(liveDraftProcessors, detectedFields); + }, [liveDraftProcessors, simulation, simulationError]); + + const hasLiveChanges = !isEmpty(liveDraftProcessors); + return { + hasLiveChanges, isLoading: isLoadingSamples || isLoadingSimulation, - error: error as IHttpFetchError | undefined, + error: simulationError as IHttpFetchError | undefined, refreshSamples, - simulate, - simulation: value, - samples: sampleDocs, + simulation, + samples: sampleDocs ?? [], + tableColumns, + watchProcessor, }; }; + +const composeSamplingCondition = ( + processors: ProcessorDefinitionWithUIAttributes[] +): Condition | undefined => { + if (isEmpty(processors)) { + return undefined; + } + + const uniqueFields = uniq(getSourceFields(processors)); + + const conditions = uniqueFields.map((field) => ({ + field, + operator: 'exists' as UnaryOperator, + })); + + return { or: conditions }; +}; + +const getSourceFields = (processors: ProcessorDefinitionWithUIAttributes[]): string[] => { + return processors.map((processor) => getProcessorConfig(processor).field); +}; + +const getTableColumns = ( + processors: ProcessorDefinitionWithUIAttributes[], + fields: DetectedField[] +) => { + const uniqueProcessorsFields = getSourceFields(processors).map((name) => ({ + name, + origin: 'processor', + })); + + const uniqueDetectedFields = fields.map((field) => ({ + name: field.name, + origin: 'detected', + })); + + return uniqBy([...uniqueProcessorsFields, ...uniqueDetectedFields], 'name') as TableColumn[]; +}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/index.tsx index c0f5644eb1320..b629fb6a42b08 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; import { dynamic } from '@kbn/shared-ux-utility'; -import { ReadStreamDefinition } from '@kbn/streams-schema'; +import { IngestStreamGetResponse } from '@kbn/streams-schema'; const StreamDetailEnrichmentContent = dynamic(() => import(/* webpackChunkName: "management_enrichment" */ './page_content').then((mod) => ({ @@ -15,7 +15,7 @@ const StreamDetailEnrichmentContent = dynamic(() => ); interface StreamDetailEnrichmentProps { - definition?: ReadStreamDefinition; + definition?: IngestStreamGetResponse; refreshDefinition: () => void; } diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/page_content.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/page_content.tsx index 94f165dfa0c21..3583779027d63 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/page_content.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/page_content.tsx @@ -5,31 +5,40 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { DragDropContextProps, EuiPanel, - EuiSpacer, + EuiResizableContainer, + EuiSplitPanel, EuiText, EuiTitle, euiDragDropReorder, + useEuiShadow, + useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ReadStreamDefinition, isRootStreamDefinition } from '@kbn/streams-schema'; -import { useBoolean } from '@kbn/react-hooks'; +import { IngestStreamGetResponse, isRootStreamDefinition } from '@kbn/streams-schema'; import { useUnsavedChangesPrompt } from '@kbn/unsaved-changes-prompt'; -import { EnrichmentEmptyPrompt } from './enrichment_empty_prompt'; -import { AddProcessorButton } from './add_processor_button'; -import { AddProcessorFlyout } from './flyout'; -import { DraggableProcessorListItem } from './processors_list'; -import { ManagementBottomBar } from '../management_bottom_bar'; -import { SortableList } from './sortable_list'; -import { useDefinition } from './hooks/use_definition'; +import { css } from '@emotion/react'; +import { isEmpty } from 'lodash'; +import { UseDefinitionReturn, useDefinition } from './hooks/use_definition'; import { useKibana } from '../../hooks/use_kibana'; import { RootStreamEmptyPrompt } from './root_stream_empty_prompt'; +import { DraggableProcessorListItem } from './processors_list'; +import { SortableList } from './sortable_list'; +import { ManagementBottomBar } from '../management_bottom_bar'; +import { AddProcessorPanel } from './processors'; +import { SimulationPlayground } from './simulation_playground'; +import { + UseProcessingSimulatorReturn, + useProcessingSimulator, +} from './hooks/use_processing_simulator'; + +const MemoSimulationPlayground = React.memo(SimulationPlayground); interface StreamDetailEnrichmentContentProps { - definition: ReadStreamDefinition; + definition: IngestStreamGetResponse; refreshDefinition: () => void; } @@ -39,9 +48,6 @@ export function StreamDetailEnrichmentContent({ }: StreamDetailEnrichmentContentProps) { const { appParams, core } = useKibana(); - const [isBottomBarOpen, { on: openBottomBar, off: closeBottomBar }] = useBoolean(); - const [isAddProcessorOpen, { on: openAddProcessor, off: closeAddProcessor }] = useBoolean(); - const { processors, addProcessor, @@ -49,110 +55,196 @@ export function StreamDetailEnrichmentContent({ deleteProcessor, resetChanges, saveChanges, - setProcessors, + reorderProcessors, hasChanges, isSavingChanges, } = useDefinition(definition, refreshDefinition); - const handlerItemDrag: DragDropContextProps['onDragEnd'] = ({ source, destination }) => { - if (source && destination) { - const items = euiDragDropReorder(processors, source.index, destination.index); - setProcessors(items); - } - }; - - useEffect(() => { - if (hasChanges) openBottomBar(); - else closeBottomBar(); - }, [closeBottomBar, hasChanges, openBottomBar]); + const { + hasLiveChanges, + isLoading, + refreshSamples, + samples, + simulation, + tableColumns, + watchProcessor, + } = useProcessingSimulator({ definition, processors }); useUnsavedChangesPrompt({ - hasUnsavedChanges: hasChanges, + hasUnsavedChanges: hasChanges || hasLiveChanges, history: appParams.history, http: core.http, navigateToUrl: core.application.navigateToUrl, openConfirm: core.overlays.openConfirm, }); - const handleSaveChanges = async () => { - await saveChanges(); - closeBottomBar(); - }; - - const handleDiscardChanges = async () => { - await resetChanges(); - closeBottomBar(); - }; - - const bottomBar = isBottomBarOpen && ( - - ); - - const addProcessorFlyout = isAddProcessorOpen && ( - - ); - - const hasProcessors = processors.length > 0; - if (isRootStreamDefinition(definition.stream)) { return ; } return ( - <> - {hasProcessors ? ( - - - - - {processors.map((processor, idx) => ( - - ))} - - - - - ) : ( - - )} - {addProcessorFlyout} - {bottomBar} - + + + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + + + + + + + + + + )} + + + + + + ); } -const ProcessorsHeader = () => { - return ( - <> - -

- {i18n.translate('xpack.streams.streamDetailView.managementTab.enrichment.headingTitle', { - defaultMessage: 'Processors for field extraction', - })} -

-
- - {i18n.translate('xpack.streams.streamDetailView.managementTab.enrichment.headingSubtitle', { - defaultMessage: - 'Use processors to transform data before indexing. Drag and drop existing processors to update their execution order.', - })} - - - ); -}; +interface ProcessorsEditorProps { + definition: IngestStreamGetResponse; + processors: UseDefinitionReturn['processors']; + onAddProcessor: UseDefinitionReturn['addProcessor']; + onDeleteProcessor: UseDefinitionReturn['deleteProcessor']; + onReorderProcessor: UseDefinitionReturn['reorderProcessors']; + onUpdateProcessor: UseDefinitionReturn['updateProcessor']; + onWatchProcessor: UseProcessingSimulatorReturn['watchProcessor']; +} + +const ProcessorsEditor = React.memo( + ({ + definition, + processors, + onAddProcessor, + onDeleteProcessor, + onReorderProcessor, + onUpdateProcessor, + onWatchProcessor, + }: ProcessorsEditorProps) => { + const { euiTheme } = useEuiTheme(); + + const handlerItemDrag: DragDropContextProps['onDragEnd'] = ({ source, destination }) => { + if (source && destination) { + const items = euiDragDropReorder(processors, source.index, destination.index); + onReorderProcessor(items); + } + }; + + const hasProcessors = !isEmpty(processors); + + return ( + <> + + +

+ {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.headingTitle', + { + defaultMessage: 'Processors for field extraction', + } + )} +

+
+ + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.headingSubtitle', + { + defaultMessage: + 'Drag and drop existing processors to update their execution order.', + } + )} + +
+ + {hasProcessors && ( + + {processors.map((processor, idx) => ( + + ))} + + )} + + + + ); + } +); + +const verticalFlexCss = css` + display: flex; + flex-direction: column; +`; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processor_outcome_preview.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processor_outcome_preview.tsx new file mode 100644 index 0000000000000..d41362b0eefd1 --- /dev/null +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processor_outcome_preview.tsx @@ -0,0 +1,247 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo, useState } from 'react'; +import { useDateRange } from '@kbn/observability-utils-browser/hooks/use_date_range'; +import { + EuiFlexGroup, + EuiFilterButton, + EuiFilterGroup, + EuiEmptyPrompt, + EuiFlexItem, + EuiSpacer, + EuiProgress, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { TimeRange } from '@kbn/es-query'; +import { flattenObject } from '@kbn/object-utils'; +import { isEmpty } from 'lodash'; +import { useKibana } from '../../hooks/use_kibana'; +import { StreamsAppSearchBar, StreamsAppSearchBarProps } from '../streams_app_search_bar'; +import { PreviewTable } from '../preview_table'; +import { TableColumn, UseProcessingSimulatorReturn } from './hooks/use_processing_simulator'; +import { AssetImage } from '../asset_image'; + +interface ProcessorOutcomePreviewProps { + columns: TableColumn[]; + isLoading: UseProcessingSimulatorReturn['isLoading']; + simulation: UseProcessingSimulatorReturn['simulation']; + samples: UseProcessingSimulatorReturn['samples']; + onRefreshSamples: UseProcessingSimulatorReturn['refreshSamples']; +} + +export const ProcessorOutcomePreview = ({ + columns, + isLoading, + simulation, + samples, + onRefreshSamples, +}: ProcessorOutcomePreviewProps) => { + const { dependencies } = useKibana(); + const { data } = dependencies.start; + + const { timeRange, setTimeRange } = useDateRange({ data }); + + const [selectedDocsFilter, setSelectedDocsFilter] = + useState('outcome_filter_all'); + + const simulationDocuments = useMemo(() => { + if (!simulation?.documents) { + return samples.map((doc) => flattenObject(doc)); + } + + const filterDocuments = (filter: DocsFilterOption) => { + switch (filter) { + case 'outcome_filter_matched': + return simulation.documents.filter((doc) => doc.isMatch); + case 'outcome_filter_unmatched': + return simulation.documents.filter((doc) => !doc.isMatch); + case 'outcome_filter_all': + default: + return simulation.documents; + } + }; + + return filterDocuments(selectedDocsFilter).map((doc) => doc.value); + }, [samples, simulation?.documents, selectedDocsFilter]); + + const tableColumns = useMemo(() => { + switch (selectedDocsFilter) { + case 'outcome_filter_unmatched': + return columns + .filter((column) => column.origin === 'processor') + .map((column) => column.name); + case 'outcome_filter_matched': + case 'outcome_filter_all': + default: + return columns.map((column) => column.name); + } + }, [columns, selectedDocsFilter]); + + return ( + <> + + + + + + {isLoading && } + + ); +}; + +const docsFilterOptions = { + outcome_filter_all: { + id: 'outcome_filter_all', + label: i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.outcomeControls.all', + { defaultMessage: 'All samples' } + ), + }, + outcome_filter_matched: { + id: 'outcome_filter_matched', + label: i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.outcomeControls.matched', + { defaultMessage: 'Matched' } + ), + }, + outcome_filter_unmatched: { + id: 'outcome_filter_unmatched', + label: i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.outcomeControls.unmatched', + { defaultMessage: 'Unmatched' } + ), + }, +} as const; + +type DocsFilterOption = keyof typeof docsFilterOptions; + +interface OutcomeControlsProps { + docsFilter: DocsFilterOption; + timeRange: TimeRange; + onDocsFilterChange: (filter: DocsFilterOption) => void; + onTimeRangeChange: (timeRange: TimeRange) => void; + onTimeRangeRefresh: () => void; + simulationFailureRate?: number; + simulationSuccessRate?: number; +} + +const OutcomeControls = ({ + docsFilter, + timeRange, + onDocsFilterChange, + onTimeRangeChange, + onTimeRangeRefresh, + simulationFailureRate, + simulationSuccessRate, +}: OutcomeControlsProps) => { + const handleQuerySubmit: StreamsAppSearchBarProps['onQuerySubmit'] = ( + { dateRange }, + isUpdate + ) => { + if (!isUpdate) { + return onTimeRangeRefresh(); + } + + if (dateRange) { + onTimeRangeChange({ + from: dateRange.from, + to: dateRange?.to, + mode: dateRange.mode, + }); + } + }; + + const getFilterButtonPropsFor = (filterId: DocsFilterOption) => ({ + hasActiveFilters: docsFilter === filterId, + onClick: () => onDocsFilterChange(filterId), + }); + + return ( + + + + {docsFilterOptions.outcome_filter_all.label} + + + {docsFilterOptions.outcome_filter_matched.label} + + + {docsFilterOptions.outcome_filter_unmatched.label} + + + + + ); +}; + +interface OutcomePreviewTableProps { + documents: Array>; + columns: string[]; +} + +const OutcomePreviewTable = ({ documents, columns }: OutcomePreviewTableProps) => { + if (isEmpty(documents)) { + return ( + } + title={ +

+ {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.rootStreamEmptyPrompt.noDataTitle', + { defaultMessage: 'Unable to generate a preview' } + )} +

+ } + body={ +

+ {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.outcomePreviewTable.noDataBody', + { + defaultMessage: + "There are no sample documents to test the processors. Try updating the time range or ingesting more data, it might be possible we could not find any matching documents with the processors' source fields.", + } + )} +

+ } + /> + ); + } + + return ; +}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/dissect_append_separator.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/dissect_append_separator.tsx similarity index 92% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/dissect_append_separator.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/dissect_append_separator.tsx index 7951636a5b59e..b0565d20efae1 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/dissect_append_separator.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/dissect_append_separator.tsx @@ -18,12 +18,12 @@ export const DissectAppendSeparator = () => { return ( "" }} /> diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/dissect_pattern_definition.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/dissect_pattern_definition.tsx similarity index 91% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/dissect_pattern_definition.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/dissect_pattern_definition.tsx index 679bb99b34004..87f5239f9e356 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/dissect_pattern_definition.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/dissect_pattern_definition.tsx @@ -22,7 +22,7 @@ export const DissectPatternDefinition = () => { name: 'pattern', rules: { required: i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.dissectPatternRequiredError', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.dissectPatternRequiredError', { defaultMessage: 'A pattern is required.' } ), }, @@ -33,12 +33,12 @@ export const DissectPatternDefinition = () => { return ( { href={esDocUrl} > {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.dissectPatternDefinitionsLink', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.dissectPatternDefinitionsLink', { defaultMessage: 'key modifier' } )} @@ -68,7 +68,7 @@ export const DissectPatternDefinition = () => { height={75} options={{ minimap: { enabled: false } }} aria-label={i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.dissectPatternDefinitionsAriaLabel', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.dissectPatternDefinitionsAriaLabel', { defaultMessage: 'Pattern editor' } )} /> diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/index.tsx similarity index 100% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/dissect/index.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/dissect/index.tsx diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/grok_pattern_definition.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/grok_pattern_definition.tsx similarity index 95% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/grok_pattern_definition.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/grok_pattern_definition.tsx index b0a9bd50b95eb..58994ace8699d 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/grok_pattern_definition.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/grok_pattern_definition.tsx @@ -20,11 +20,11 @@ export const GrokPatternDefinition = () => { return ( { languageId="xjson" height={200} aria-label={i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.grokPatternDefinitionsAriaLabel', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.grokPatternDefinitionsAriaLabel', { defaultMessage: 'Pattern definitions editor' } )} /> diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/grok_patterns_editor.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/grok_patterns_editor.tsx similarity index 91% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/grok_patterns_editor.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/grok_patterns_editor.tsx index 78a4f735adcef..9e6f93543e7b7 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/grok_patterns_editor.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/grok_patterns_editor.tsx @@ -17,7 +17,6 @@ import { DragDropContextProps, EuiFormRow, EuiPanel, - EuiSpacer, EuiButtonEmpty, EuiDraggable, EuiFlexGroup, @@ -62,11 +61,11 @@ export const GrokPatternsEditor = () => { return ( - + {fieldsWithError.map((field, idx) => ( { onRemove={getRemovePatternHandler(idx)} inputProps={register(`patterns.${idx}.value`, { required: i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.grokEditorRequiredError', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.grokEditorRequiredError', { defaultMessage: 'A pattern is required.' } ), })} /> ))} - {i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.grokEditor.addPattern', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.grokEditor.addPattern', { defaultMessage: 'Add pattern' } )} @@ -132,13 +129,13 @@ const DraggablePatternInput = ({ > {(provided) => ( - + @@ -158,7 +155,7 @@ const DraggablePatternInput = ({ color="danger" onClick={() => onRemove(idx)} aria-label={i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.grokEditor.removePattern', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.grokEditor.removePattern', { defaultMessage: 'Remove grok pattern' } )} /> diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/index.tsx similarity index 100% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/grok/index.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/grok/index.tsx diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/ignore_toggles.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/ignore_toggles.tsx similarity index 91% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/ignore_toggles.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/ignore_toggles.tsx index 88bbc0410e7c7..bb9e8b16dce99 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/ignore_toggles.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/ignore_toggles.tsx @@ -19,14 +19,14 @@ export const IgnoreFailureToggle = () => { ignore_failure, @@ -44,11 +44,11 @@ export const IgnoreMissingToggle = () => { diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/index.tsx new file mode 100644 index 0000000000000..a8c03a6085197 --- /dev/null +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/index.tsx @@ -0,0 +1,394 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + EuiButton, + EuiForm, + EuiSpacer, + EuiButtonEmpty, + EuiFlexGroup, + EuiPanel, + useEuiTheme, + EuiHorizontalRule, + EuiAccordion, + EuiButtonIcon, + EuiIcon, + EuiText, + EuiBadge, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ProcessorType, IngestStreamGetResponse } from '@kbn/streams-schema'; +import { isEqual } from 'lodash'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useForm, SubmitHandler, FormProvider, useWatch } from 'react-hook-form'; +import { css } from '@emotion/react'; +import { useBoolean } from '@kbn/react-hooks'; +import { DissectProcessorForm } from './dissect'; +import { GrokProcessorForm } from './grok'; +import { ProcessorTypeSelector } from './processor_type_selector'; +import { ProcessorFormState, ProcessorDefinitionWithUIAttributes } from '../types'; +import { + getDefaultFormState, + convertFormStateToProcessor, + isGrokProcessor, + isDissectProcessor, +} from '../utils'; +import { useDiscardConfirm } from '../../../hooks/use_discard_confirm'; +import { UseDefinitionReturn } from '../hooks/use_definition'; +import { UseProcessingSimulatorReturn } from '../hooks/use_processing_simulator'; + +export interface ProcessorPanelProps { + definition: IngestStreamGetResponse; + onWatchProcessor: UseProcessingSimulatorReturn['watchProcessor']; +} + +export interface AddProcessorPanelProps extends ProcessorPanelProps { + isInitiallyOpen?: boolean; + onAddProcessor: UseDefinitionReturn['addProcessor']; +} + +export interface EditProcessorPanelProps extends ProcessorPanelProps { + processor: ProcessorDefinitionWithUIAttributes; + onDeleteProcessor: UseDefinitionReturn['deleteProcessor']; + onUpdateProcessor: UseDefinitionReturn['updateProcessor']; +} + +export function AddProcessorPanel({ onAddProcessor, onWatchProcessor }: AddProcessorPanelProps) { + const { euiTheme } = useEuiTheme(); + + const [hasChanges, setHasChanges] = useState(false); + const [isOpen, { on: openPanel, off: closePanel }] = useBoolean(false); + + const defaultValues = useMemo(() => getDefaultFormState('grok'), []); + + const methods = useForm({ defaultValues, mode: 'onChange' }); + + const type = useWatch({ control: methods.control, name: 'type' }); + + useEffect(() => { + if (isOpen) { + const { unsubscribe } = methods.watch((value) => { + const draftProcessor = createDraftProcessorFromForm(value as ProcessorFormState); + onWatchProcessor(draftProcessor); + setHasChanges(!isEqual(defaultValues, value)); + }); + return () => unsubscribe(); + } + }, [defaultValues, isOpen, methods, onWatchProcessor]); + + const handleSubmit: SubmitHandler = async (data) => { + const processingDefinition = convertFormStateToProcessor(data); + + onWatchProcessor({ id: 'draft', deleteIfExists: true }); + onAddProcessor(processingDefinition, data.detected_fields); + closePanel(); + }; + + const handleCancel = () => { + methods.reset(); + onWatchProcessor({ id: 'draft', deleteIfExists: true }); + closePanel(); + }; + + const handleOpen = () => { + const draftProcessor = createDraftProcessorFromForm(defaultValues); + onWatchProcessor(draftProcessor); + openPanel(); + }; + + const confirmDiscardAndClose = useDiscardConfirm(handleCancel); + + const buttonContent = isOpen ? ( + i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processorPanel.addingProcessor', + { defaultMessage: 'Adding processor' } + ) + ) : ( + + + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.addProcessorAction', + { defaultMessage: 'Add a processor' } + )} + + ); + + return ( + + + + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processorPanel.cancel', + { defaultMessage: 'Cancel' } + )} + + + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processorPanel.confirmAddProcessor', + { defaultMessage: 'Add processor' } + )} + + + ) : null + } + > + + + + + + {type === 'grok' && } + {type === 'dissect' && } + + + + + ); +} + +const createDraftProcessorFromForm = ( + formState: ProcessorFormState +): ProcessorDefinitionWithUIAttributes => { + const processingDefinition = convertFormStateToProcessor(formState); + + return { + id: 'draft', + status: 'draft', + type: formState.type, + ...processingDefinition, + }; +}; + +export function EditProcessorPanel({ + onDeleteProcessor, + onUpdateProcessor, + onWatchProcessor, + processor, +}: EditProcessorPanelProps) { + const { euiTheme } = useEuiTheme(); + + const [hasChanges, setHasChanges] = useState(false); + const [isOpen, { on: openPanel, off: closePanel }] = useBoolean(); + + const processorDescription = getProcessorDescription(processor); + + const isDraft = processor.status === 'draft'; + const isUnsaved = isDraft || processor.status === 'updated'; + + const defaultValues = useMemo(() => getDefaultFormState(processor.type, processor), [processor]); + + const methods = useForm({ defaultValues, mode: 'onChange' }); + + const type = useWatch({ control: methods.control, name: 'type' }); + + useEffect(() => { + const { unsubscribe } = methods.watch((value) => { + const processingDefinition = convertFormStateToProcessor(value as ProcessorFormState); + onWatchProcessor({ + id: processor.id, + status: processor.status, + type: value.type as ProcessorType, + ...processingDefinition, + }); + setHasChanges(!isEqual(defaultValues, value)); + }); + return () => unsubscribe(); + }, [defaultValues, methods, onWatchProcessor, processor.id, processor.status]); + + const handleSubmit: SubmitHandler = (data) => { + const processorDefinition = convertFormStateToProcessor(data); + + onUpdateProcessor(processor.id, processorDefinition, isDraft ? 'draft' : 'updated'); + closePanel(); + }; + + const handleProcessorDelete = () => { + onDeleteProcessor(processor.id); + closePanel(); + }; + + const handleCancel = () => { + methods.reset(); + closePanel(); + }; + + const confirmDiscardAndClose = useDiscardConfirm(handleCancel); + const confirmDeletionAndClose = useDiscardConfirm(handleProcessorDelete, { + title: deleteProcessorTitle, + message: deleteProcessorMessage, + confirmButtonText: deleteProcessorLabel, + cancelButtonText: deleteProcessorCancelLabel, + }); + + const buttonContent = isOpen ? ( + {processor.type.toUpperCase()} + ) : ( + + + {processor.type.toUpperCase()} + + {processorDescription} + + + ); + + return ( + + + + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processorPanel.cancel', + { defaultMessage: 'Cancel' } + )} + + + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processorPanel.confirmEditProcessor', + { defaultMessage: 'Update processor' } + )} + +
+ ) : ( + + {isUnsaved && ( + + {i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.processorPanel.unsavedBadge', + { defaultMessage: 'Unsaved' } + )} + + )} + + + ) + } + > + + + + + + {type === 'grok' && } + {type === 'dissect' && } + + + {deleteProcessorLabel} + + + + + + ); +} + +const deleteProcessorLabel = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.deleteProcessorLabel', + { defaultMessage: 'Delete processor' } +); + +const deleteProcessorCancelLabel = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.deleteProcessorCancelLabel', + { defaultMessage: 'Cancel' } +); + +const deleteProcessorTitle = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.deleteProcessorTitle', + { defaultMessage: 'Are you sure you want to delete this processor?' } +); + +const deleteProcessorMessage = i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.deleteProcessorMessage', + { defaultMessage: 'Deleting this processor will permanently impact the field configuration.' } +); + +const getProcessorDescription = (processor: ProcessorDefinitionWithUIAttributes) => { + if (isGrokProcessor(processor)) { + return processor.grok.patterns.join(' • '); + } else if (isDissectProcessor(processor)) { + return processor.dissect.pattern; + } + + return ''; +}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/optional_fields_accordion.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/optional_fields_accordion.tsx similarity index 97% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/optional_fields_accordion.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/optional_fields_accordion.tsx index ddba021d10106..fc7a52af63d0e 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/optional_fields_accordion.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/optional_fields_accordion.tsx @@ -19,7 +19,7 @@ export const OptionalFieldsAccordion = ({ children }: PropsWithChildren) => { id="optionalFieldsAccordion" paddingSize="none" buttonContent={i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.optionalFields', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.optionalFields', { defaultMessage: 'Optional fields' } )} > diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_condition_editor.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/processor_condition_editor.tsx similarity index 86% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_condition_editor.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/processor_condition_editor.tsx index eca584d8d2e71..ea20d71ccff6f 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_condition_editor.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/processor_condition_editor.tsx @@ -11,7 +11,7 @@ import { ConditionEditor } from '../../condition_editor'; import { ProcessorFormState } from '../types'; export const ProcessorConditionEditor = () => { - const { field } = useController({ name: 'condition' }); + const { field } = useController({ name: 'if' }); return ; }; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_field_selector.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/processor_field_selector.tsx similarity index 92% rename from x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_field_selector.tsx rename to x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/processor_field_selector.tsx index ec9e5019fd800..d2389ea936f5d 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/flyout/processor_field_selector.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/processors/processor_field_selector.tsx @@ -16,7 +16,7 @@ export const ProcessorFieldSelector = () => { name: 'field', rules: { required: i18n.translate( - 'xpack.streams.streamDetailView.managementTab.enrichment.processorFlyout.fieldSelectorRequiredError', + 'xpack.streams.streamDetailView.managementTab.enrichment.processor.fieldSelectorRequiredError', { defaultMessage: 'A field value is required.' } ), }, @@ -28,11 +28,11 @@ export const ProcessorFieldSelector = () => { return ( @@ -73,7 +73,7 @@ const availableProcessors: TAvailableProcessors = { inputDisplay: 'Dissect', getDocUrl: (esDocUrl: string) => ( ( & { idx: number }) => ( +}: EditProcessorPanelProps & { idx: number }) => ( - {(_provided, state) => ( - - )} + {() => } ); - -interface ProcessorListItemProps { - definition: ReadStreamDefinition; - processor: EnrichmentUIProcessorDefinition; - hasShadow: EuiPanelProps['hasShadow']; - onUpdateProcessor: EditProcessorFlyoutProps['onUpdateProcessor']; - onDeleteProcessor: EditProcessorFlyoutProps['onDeleteProcessor']; -} - -const ProcessorListItem = ({ - definition, - processor, - hasShadow = false, - onUpdateProcessor, - onDeleteProcessor, -}: ProcessorListItemProps) => { - const [isEditProcessorOpen, { on: openEditProcessor, off: closeEditProcessor }] = useBoolean(); - - const type = 'grok' in processor.config ? 'grok' : 'dissect'; - const description = getProcessorDescription(processor); - - return ( - - - - - {type.toUpperCase()} - - - - {description} - - - - - {isEditProcessorOpen && ( - - )} - - ); -}; - -const getProcessorDescription = (processor: EnrichmentUIProcessorDefinition) => { - if (isGrokProcessor(processor.config)) { - return processor.config.grok.patterns.join(' • '); - } else if (isDissectProcessor(processor.config)) { - return processor.config.dissect.pattern; - } - - return ''; -}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/simulation_playground.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/simulation_playground.tsx new file mode 100644 index 0000000000000..274801b71e02d --- /dev/null +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/simulation_playground.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexItem, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; +import { IngestStreamGetResponse, isWiredStreamGetResponse } from '@kbn/streams-schema'; +import { ProcessorOutcomePreview } from './processor_outcome_preview'; +import { TableColumn, UseProcessingSimulatorReturn } from './hooks/use_processing_simulator'; + +interface SimulationPlaygroundProps { + definition: IngestStreamGetResponse; + columns: TableColumn[]; + isLoading: UseProcessingSimulatorReturn['isLoading']; + simulation: UseProcessingSimulatorReturn['simulation']; + samples: UseProcessingSimulatorReturn['samples']; + onRefreshSamples: UseProcessingSimulatorReturn['refreshSamples']; +} + +export const SimulationPlayground = (props: SimulationPlaygroundProps) => { + const { definition, columns, isLoading, simulation, samples, onRefreshSamples } = props; + + const tabs = { + dataPreview: { + name: i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.simulationPlayground.dataPreview', + { defaultMessage: 'Data preview' } + ), + }, + ...(isWiredStreamGetResponse(definition) && { + detectedFields: { + name: i18n.translate( + 'xpack.streams.streamDetailView.managementTab.enrichment.simulationPlayground.detectedFields', + { defaultMessage: 'Detected fields' } + ), + }, + }), + } as const; + + const [selectedTabId, setSelectedTabId] = useState('dataPreview'); + + return ( + <> + + + {Object.entries(tabs).map(([tabId, tab]) => ( + setSelectedTabId(tabId as keyof typeof tabs)} + > + {tab.name} + + ))} + + + + {selectedTabId === 'dataPreview' && ( + + )} + {selectedTabId === 'detectedFields' && + i18n.translate('xpack.streams.simulationPlayground.div.detectedFieldsLabel', { + defaultMessage: 'WIP', + })} + + ); +}; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/sortable_list.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/sortable_list.tsx index 4847359cec734..8a355e39ff70b 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/sortable_list.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/sortable_list.tsx @@ -29,6 +29,7 @@ export const SortableList = ({ onDragItem, children }: SortableListProps) => { droppableId="droppable-area" css={css` background-color: ${euiTheme.colors.backgroundTransparent}; + margin-bottom: ${euiTheme.size.s}; `} > {children} diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/types.ts b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/types.ts index 4f6c314901868..b0c74e79932f0 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/types.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/types.ts @@ -6,65 +6,36 @@ */ import { - Condition, DissectProcessorConfig, - FieldDefinitionConfig, + FieldDefinitionType, GrokProcessorConfig, + ProcessorDefinition, + ProcessorTypeOf, } from '@kbn/streams-schema'; -export interface DissectProcessingDefinition { - dissect: Omit; -} - -export interface GrokProcessingDefinition { - grok: Omit; -} - -export interface ProcessingDefinition { - condition: Condition; - config: DissectProcessingDefinition | GrokProcessingDefinition; -} - -export interface EnrichmentUIProcessorDefinition extends ProcessingDefinition { +export type WithUIAttributes = T & { id: string; -} + type: ProcessorTypeOf; + status: 'draft' | 'saved' | 'updated'; +}; -export interface ProcessingDefinitionGrok extends Pick { - config: GrokProcessingDefinition; -} +export type ProcessorDefinitionWithUIAttributes = WithUIAttributes; -export interface ProcessingDefinitionDissect extends Pick { - config: DissectProcessingDefinition; +export interface DetectedField { + name: string; + type: FieldDefinitionType | 'unmapped'; } -interface BaseFormState extends Pick { +interface BaseFormState { detected_fields?: DetectedField[]; } export type GrokFormState = BaseFormState & - Omit & { + Omit & { type: 'grok'; patterns: Array<{ value: string }>; }; -export type DissectFormState = DissectProcessingDefinition['dissect'] & - BaseFormState & { type: 'dissect' }; +export type DissectFormState = BaseFormState & DissectProcessorConfig & { type: 'dissect' }; export type ProcessorFormState = GrokFormState | DissectFormState; - -export interface DetectedField { - name: string; - type: FieldDefinitionConfig['type'] | 'unmapped'; -} - -export function isGrokProcessor( - config: ProcessingDefinition['config'] -): config is GrokProcessingDefinition { - return 'grok' in config; -} - -export function isDissectProcessor( - config: ProcessingDefinition['config'] -): config is DissectProcessingDefinition { - return 'dissect' in config; -} diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/utils.ts b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/utils.ts index 02f09a363ef5a..ff7de56bc7f69 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/utils.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_enrichment/utils.ts @@ -7,21 +7,17 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { ProcessorType, conditionSchema, createIsNarrowSchema } from '@kbn/streams-schema'; +import { ProcessorDefinition, ProcessorType, getProcessorType } from '@kbn/streams-schema'; +import { htmlIdGenerator } from '@elastic/eui'; import { isEmpty } from 'lodash'; -import { z } from '@kbn/zod'; import { DissectFormState, - DissectProcessingDefinition, - EnrichmentUIProcessorDefinition, + ProcessorDefinitionWithUIAttributes, GrokFormState, - GrokProcessingDefinition, - ProcessingDefinition, ProcessorFormState, - isDissectProcessor, - isGrokProcessor, + WithUIAttributes, } from './types'; -import { EMPTY_EQUALS_CONDITION } from '../../util/condition'; +import { ALWAYS_CONDITION } from '../../util/condition'; const defaultGrokProcessorFormState: GrokFormState = { type: 'grok', @@ -30,7 +26,7 @@ const defaultGrokProcessorFormState: GrokFormState = { pattern_definitions: {}, ignore_failure: true, ignore_missing: true, - condition: EMPTY_EQUALS_CONDITION, + if: ALWAYS_CONDITION, }; const defaultDissectProcessorFormState: DissectFormState = { @@ -39,7 +35,7 @@ const defaultDissectProcessorFormState: DissectFormState = { pattern: '', ignore_failure: true, ignore_missing: true, - condition: EMPTY_EQUALS_CONDITION, + if: ALWAYS_CONDITION, }; const defaultProcessorFormStateByType: Record = { @@ -49,67 +45,59 @@ const defaultProcessorFormStateByType: Record export const getDefaultFormState = ( type: ProcessorType, - processor?: EnrichmentUIProcessorDefinition + processor?: ProcessorDefinitionWithUIAttributes ): ProcessorFormState => { if (!processor) return defaultProcessorFormStateByType[type]; - if (isGrokProcessor(processor.config)) { - const { grok } = processor.config; + if (isGrokProcessor(processor)) { + const { grok } = processor; return structuredClone({ ...grok, - condition: processor.condition, type: 'grok', patterns: grok.patterns.map((pattern) => ({ value: pattern })), }); } - const { dissect } = processor.config; + if (isDissectProcessor(processor)) { + const { dissect } = processor; - return structuredClone({ - ...dissect, - condition: processor.condition, - type: 'dissect', - }); + return structuredClone({ + ...dissect, + type: 'dissect', + }); + } + + throw new Error(`Default state not found for unsupported processor type: ${type}`); }; -export const convertFormStateToProcessing = ( - formState: ProcessorFormState -): ProcessingDefinition => { +export const convertFormStateToProcessor = (formState: ProcessorFormState): ProcessorDefinition => { if (formState.type === 'grok') { - const { condition, patterns, field, pattern_definitions, ignore_failure, ignore_missing } = - formState; + const { patterns, field, pattern_definitions, ignore_failure, ignore_missing } = formState; return { - condition, - config: { - grok: { - patterns: patterns - .filter(({ value }) => value.trim().length > 0) - .map(({ value }) => value), - field, - pattern_definitions, - ignore_failure, - ignore_missing, - }, + grok: { + if: formState.if, + patterns: patterns.filter(({ value }) => value.trim().length > 0).map(({ value }) => value), + field, + pattern_definitions, + ignore_failure, + ignore_missing, }, }; } if (formState.type === 'dissect') { - const { condition, field, pattern, append_separator, ignore_failure, ignore_missing } = - formState; + const { field, pattern, append_separator, ignore_failure, ignore_missing } = formState; return { - condition, - config: { - dissect: { - field, - pattern, - append_separator, - ignore_failure, - ignore_missing, - }, + dissect: { + if: formState.if, + field, + pattern, + append_separator: isEmpty(append_separator) ? undefined : append_separator, + ignore_failure, + ignore_missing, }, }; } @@ -117,27 +105,36 @@ export const convertFormStateToProcessing = ( throw new Error('Cannot convert form state to processing: unknown type.'); }; -export const isCompleteCondition = createIsNarrowSchema(z.unknown(), conditionSchema); - -export const isCompleteGrokDefinition = (processing: GrokProcessingDefinition) => { - const { patterns } = processing.grok; - - return !isEmpty(patterns); -}; - -export const isCompleteDissectDefinition = (processing: DissectProcessingDefinition) => { - const { pattern } = processing.dissect; - - return !isEmpty(pattern); +const createProcessorGuardByType = + (type: TProcessorType) => + ( + processor: ProcessorDefinitionWithUIAttributes + ): processor is WithUIAttributes< + Extract + > => + processor.type === type; + +export const isGrokProcessor = createProcessorGuardByType('grok'); +export const isDissectProcessor = createProcessorGuardByType('dissect'); + +const createId = htmlIdGenerator(); +const toUIDefinition = ( + processor: TProcessorDefinition, + uiAttributes: Partial, 'status'>> = {} +): ProcessorDefinitionWithUIAttributes => ({ + id: createId(), + status: 'saved', + type: getProcessorType(processor), + ...uiAttributes, + ...processor, +}); + +const toAPIDefinition = (processor: ProcessorDefinitionWithUIAttributes): ProcessorDefinition => { + const { id, status, type, ...processorConfig } = processor; + return processorConfig; }; -export const isCompleteProcessingDefinition = (processing: ProcessingDefinition) => { - if (isGrokProcessor(processing.config)) { - return isCompleteGrokDefinition(processing.config); - } - if (isDissectProcessor(processing.config)) { - return isCompleteDissectDefinition(processing.config); - } - - return false; +export const processorConverter = { + toAPIDefinition, + toUIDefinition, }; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/classic.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/classic.tsx index 5d22b05e4d8b4..7d65d5727dd28 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/classic.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/classic.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { UnwiredStreamGetResponse } from '@kbn/streams-schema'; import { EuiCallOut, EuiFlexGroup, EuiListGroup, EuiText } from '@elastic/eui'; @@ -31,23 +31,6 @@ export function ClassicStreamDetailManagement({ path: { key, subtab }, } = useStreamsAppParams('/{key}/management/{subtab}'); - const legacyDefinition = useMemo(() => { - if (!definition) { - return undefined; - } - return { - dashboards: definition.dashboards, - elasticsearch_assets: definition.elasticsearch_assets, - inherited_fields: {}, - effective_lifecycle: definition.effective_lifecycle, - name: definition.stream.name, - data_stream_exists: definition.data_stream_exists, - stream: { - ...definition.stream, - }, - }; - }, [definition]); - const tabs: ManagementTabs = { overview: { content: , @@ -60,10 +43,7 @@ export function ClassicStreamDetailManagement({ if (definition.data_stream_exists) { tabs.enrich = { content: ( - + ), label: i18n.translate('xpack.streams.streamDetailView.enrichmentTab', { defaultMessage: 'Extract field', diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/wired.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/wired.tsx index 13859445bd634..24c9ff966bb1b 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/wired.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_management/wired.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useMemo } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { WiredStreamGetResponse } from '@kbn/streams-schema'; import { useStreamsAppParams } from '../../hooks/use_streams_app_params'; @@ -33,22 +33,6 @@ export function WiredStreamDetailManagement({ path: { key, subtab }, } = useStreamsAppParams('/{key}/management/{subtab}'); - const legacyDefinition = useMemo(() => { - if (!definition) { - return undefined; - } - return { - dashboards: definition.dashboards, - inherited_fields: definition.inherited_fields, - elasticsearch_assets: [], - effective_lifecycle: definition.effective_lifecycle, - name: definition.stream.name, - stream: { - ...definition.stream, - }, - }; - }, [definition]); - const tabs = { route: { content: ( @@ -60,10 +44,7 @@ export function WiredStreamDetailManagement({ }, enrich: { content: ( - + ), label: i18n.translate('xpack.streams.streamDetailView.enrichmentTab', { defaultMessage: 'Extract field', diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_overview/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_overview/index.tsx index 9050d234ceffc..801eff50b4f12 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_overview/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_overview/index.tsx @@ -21,7 +21,7 @@ import React, { useMemo } from 'react'; import { css } from '@emotion/css'; import { IngestStreamGetResponse, - isUnWiredStreamGetResponse, + isUnwiredStreamGetResponse, isWiredStreamDefinition, } from '@kbn/streams-schema'; import { useDateRange } from '@kbn/observability-utils-browser/hooks/use_date_range'; @@ -132,7 +132,7 @@ export function StreamDetailOverview({ definition }: { definition?: IngestStream async ({ signal }) => { if ( !definition || - (isUnWiredStreamGetResponse(definition) && !definition.data_stream_exists) + (isUnwiredStreamGetResponse(definition) && !definition.data_stream_exists) ) { return undefined; } @@ -306,7 +306,7 @@ function ChildStreamList({ definition }: { definition?: IngestStreamGetResponse [streamsRepositoryClient] ); - const childDefinitions = useMemo(() => { + const childrenStreams = useMemo(() => { if (!definition) { return []; } @@ -315,7 +315,7 @@ function ChildStreamList({ definition }: { definition?: IngestStreamGetResponse ); }, [definition, streamsListFetch.value?.streams]); - if (definition && childDefinitions?.length === 1) { + if (definition && childrenStreams?.length === 1) { return ( @@ -361,5 +361,5 @@ function ChildStreamList({ definition }: { definition?: IngestStreamGetResponse ); } - return ; + return ; } diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_routing/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_routing/index.tsx index 43e6839bacf4c..0b539f0d5b636 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_routing/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_routing/index.tsx @@ -36,7 +36,6 @@ import { useAbortController } from '@kbn/observability-utils-browser/hooks/use_a import { useDateRange } from '@kbn/observability-utils-browser/hooks/use_date_range'; import React, { useCallback, useEffect } from 'react'; import { - ReadStreamDefinition, isRoot, isDescendantOf, RoutingDefinition, @@ -93,9 +92,9 @@ function useRoutingState({ ); // Child streams: either represents the child streams as they are, or the new order from drag and drop. - const [childStreams, setChildStreams] = React.useState< - ReadStreamDefinition['stream']['ingest']['routing'] - >(definition?.stream.ingest.routing ?? []); + const [childStreams, setChildStreams] = React.useState( + definition?.stream.ingest.routing ?? [] + ); useEffect(() => { setChildStreams(definition?.stream.ingest.routing ?? []); diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_view/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_view/index.tsx index 59181dbd080c8..154bf202a4e75 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_view/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_detail_view/index.tsx @@ -5,6 +5,7 @@ * 2.0. */ import { i18n } from '@kbn/i18n'; +import { isUnwiredStreamGetResponse, isWiredStreamGetResponse } from '@kbn/streams-schema'; import React from 'react'; import { useKibana } from '../../hooks/use_kibana'; import { useStreamsAppFetch } from '../../hooks/use_streams_app_fetch'; @@ -35,15 +36,45 @@ export function StreamDetailView() { refresh, loading, } = useStreamsAppFetch( - ({ signal }) => { - return streamsRepositoryClient.fetch('GET /api/streams/{id}', { - signal, - params: { - path: { - id: key, + async ({ signal }) => { + return streamsRepositoryClient + .fetch('GET /api/streams/{id}', { + signal, + params: { + path: { + id: key, + }, }, - }, - }); + }) + .then((response) => { + if (isWiredStreamGetResponse(response)) { + return { + dashboards: response.dashboards, + inherited_fields: response.inherited_fields, + elasticsearch_assets: [], + effective_lifecycle: response.effective_lifecycle, + name: key, + stream: { + ...response.stream, + }, + }; + } + + if (isUnwiredStreamGetResponse(response)) { + return { + dashboards: response.dashboards, + elasticsearch_assets: response.elasticsearch_assets, + inherited_fields: {}, + effective_lifecycle: response.effective_lifecycle, + name: key, + data_stream_exists: response.data_stream_exists, + stream: { + ...response.stream, + }, + }; + } + throw new Error('Stream detail only supports IngestStreams.'); + }); }, [streamsRepositoryClient, key] ); diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_list_view/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_list_view/index.tsx index 9e727363456d6..c1e81281f639c 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/stream_list_view/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/stream_list_view/index.tsx @@ -61,7 +61,7 @@ export function StreamListView() { /> - + diff --git a/x-pack/solutions/observability/plugins/streams_app/public/components/streams_list/index.tsx b/x-pack/solutions/observability/plugins/streams_app/public/components/streams_list/index.tsx index e30988bac32c4..430954845d468 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/components/streams_list/index.tsx +++ b/x-pack/solutions/observability/plugins/streams_app/public/components/streams_list/index.tsx @@ -35,31 +35,29 @@ import { getIndexPatterns } from '../../util/hierarchy_helpers'; export interface StreamTree { name: string; type: 'wired' | 'root' | 'classic'; - definition: StreamDefinition; + stream: StreamDefinition; children: StreamTree[]; } -function asTrees(definitions: StreamDefinition[]) { +function asTrees(streams: StreamDefinition[]) { const trees: StreamTree[] = []; - const wiredDefinitions = definitions.filter((definition) => isWiredStreamDefinition(definition)); - wiredDefinitions.sort((a, b) => getSegments(a.name).length - getSegments(b.name).length); + const wiredStreams = streams.filter(isWiredStreamDefinition); + wiredStreams.sort((a, b) => getSegments(a.name).length - getSegments(b.name).length); - wiredDefinitions.forEach((definition) => { + wiredStreams.forEach((stream) => { let currentTree = trees; let existingNode: StreamTree | undefined; - const segments = getSegments(definition.name); + const segments = getSegments(stream.name); // traverse the tree following the prefix of the current id. // once we reach the leaf, the current id is added as child - this works because the ids are sorted by depth - while ( - (existingNode = currentTree.find((node) => isDescendantOf(node.name, definition.name))) - ) { + while ((existingNode = currentTree.find((node) => isDescendantOf(node.name, stream.name)))) { currentTree = existingNode.children; } if (!existingNode) { const newNode: StreamTree = { - name: definition.name, + name: stream.name, children: [], - definition, + stream, type: segments.length === 1 ? 'root' : 'wired', }; currentTree.push(newNode); @@ -70,19 +68,19 @@ function asTrees(definitions: StreamDefinition[]) { } export function StreamsList({ - definitions, + streams, query, showControls, }: { - definitions: StreamDefinition[] | undefined; + streams: StreamDefinition[] | undefined; query?: string; showControls: boolean; }) { const [collapsed, setCollapsed] = React.useState>({}); const [showClassic, setShowClassic] = React.useState(true); const items = useMemo(() => { - return definitions ?? []; - }, [definitions]); + return streams ?? []; + }, [streams]); const filteredItems = useMemo(() => { return items @@ -96,10 +94,10 @@ export function StreamsList({ const treeView = useMemo(() => { const trees = asTrees(filteredItems); - const classicList = classicStreams.map((definition) => ({ - name: definition.name, + const classicList = classicStreams.map((stream) => ({ + name: stream.name, type: 'classic' as const, - definition, + stream, children: [], })); return [...trees, ...classicList]; @@ -190,7 +188,7 @@ function StreamNode({ ); const discoverUrl = useMemo(() => { - const indexPatterns = getIndexPatterns(node.definition); + const indexPatterns = getIndexPatterns(node.stream); if (!discoverLocator || !indexPatterns) { return undefined; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/hooks/use_discard_confirm.ts b/x-pack/solutions/observability/plugins/streams_app/public/hooks/use_discard_confirm.ts index 8e26e5620a0a9..21ddf1e5ee3a1 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/hooks/use_discard_confirm.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/hooks/use_discard_confirm.ts @@ -6,30 +6,35 @@ */ import { i18n } from '@kbn/i18n'; +import { OverlayModalConfirmOptions } from '@kbn/core/public'; import { useKibana } from './use_kibana'; -export const useDiscardConfirm = (handler: () => void) => { +const defaultMessage = i18n.translate('xpack.streams.cancelModal.message', { + defaultMessage: 'Are you sure you want to discard your changes?', +}); + +export const useDiscardConfirm = any>( + handler: THandler, + options: OverlayModalConfirmOptions & { message?: string } = {} +) => { const { core } = useKibana(); + const { message = defaultMessage, ...optionsOverride } = options; - return async () => { - const hasCancelled = await core.overlays.openConfirm( - i18n.translate('xpack.streams.cancelModal.message', { - defaultMessage: 'Are you sure you want to discard your changes?', + return async (...args: Parameters) => { + const hasCancelled = await core.overlays.openConfirm(message, { + buttonColor: 'danger', + title: i18n.translate('xpack.streams.cancelModal.title', { + defaultMessage: 'Discard changes?', + }), + confirmButtonText: i18n.translate('xpack.streams.cancelModal.confirm', { + defaultMessage: 'Discard', + }), + cancelButtonText: i18n.translate('xpack.streams.cancelModal.cancel', { + defaultMessage: 'Keep editing', }), - { - buttonColor: 'danger', - title: i18n.translate('xpack.streams.cancelModal.title', { - defaultMessage: 'Discard changes?', - }), - confirmButtonText: i18n.translate('xpack.streams.cancelModal.confirm', { - defaultMessage: 'Discard', - }), - cancelButtonText: i18n.translate('xpack.streams.cancelModal.cancel', { - defaultMessage: 'Keep editing', - }), - } - ); + ...optionsOverride, + }); - if (hasCancelled) handler(); + if (hasCancelled) handler(...args); }; }; diff --git a/x-pack/solutions/observability/plugins/streams_app/public/util/condition.ts b/x-pack/solutions/observability/plugins/streams_app/public/util/condition.ts index ba4d5b8001761..bd3370f48d4d3 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/util/condition.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/util/condition.ts @@ -19,6 +19,8 @@ export const EMPTY_EQUALS_CONDITION: BinaryFilterCondition = Object.freeze({ value: '', }); +export const ALWAYS_CONDITION: AlwaysCondition = Object.freeze({ always: {} }); + export function alwaysToEmptyEquals(condition: T): Exclude; export function alwaysToEmptyEquals(condition: Condition) { @@ -30,7 +32,7 @@ export function alwaysToEmptyEquals(condition: Condition) { export function emptyEqualsToAlways(condition: Condition) { if (isEqual(condition, EMPTY_EQUALS_CONDITION)) { - return { always: {} }; + return ALWAYS_CONDITION; } return condition; } diff --git a/x-pack/solutions/observability/plugins/streams_app/public/util/hierarchy_helpers.ts b/x-pack/solutions/observability/plugins/streams_app/public/util/hierarchy_helpers.ts index 188050e4c594e..d74d18085882a 100644 --- a/x-pack/solutions/observability/plugins/streams_app/public/util/hierarchy_helpers.ts +++ b/x-pack/solutions/observability/plugins/streams_app/public/util/hierarchy_helpers.ts @@ -7,15 +7,15 @@ import { StreamDefinition, isUnwiredStreamDefinition } from '@kbn/streams-schema'; -export function getIndexPatterns(definition: StreamDefinition | undefined) { - if (!definition) { +export function getIndexPatterns(stream: StreamDefinition | undefined) { + if (!stream) { return undefined; } - if (!isUnwiredStreamDefinition(definition)) { - return [definition.name]; + if (!isUnwiredStreamDefinition(stream)) { + return [stream.name]; } - const isRoot = definition.name.indexOf('.') === -1; - const dataStreamOfDefinition = definition.name; + const isRoot = stream.name.indexOf('.') === -1; + const dataStreamOfDefinition = stream.name; return isRoot ? [dataStreamOfDefinition, `${dataStreamOfDefinition}.*`] : [`${dataStreamOfDefinition}*`]; diff --git a/x-pack/solutions/observability/plugins/streams_app/tsconfig.json b/x-pack/solutions/observability/plugins/streams_app/tsconfig.json index 5b21ca3789374..8e8ab46705baa 100644 --- a/x-pack/solutions/observability/plugins/streams_app/tsconfig.json +++ b/x-pack/solutions/observability/plugins/streams_app/tsconfig.json @@ -57,7 +57,6 @@ "@kbn/deeplinks-analytics", "@kbn/dashboard-plugin", "@kbn/react-kibana-mount", - "@kbn/fields-metadata-plugin", - "@kbn/zod" + "@kbn/fields-metadata-plugin" ] } diff --git a/x-pack/solutions/observability/plugins/synthetics/e2e/synthetics/journeys/alert_rules/custom_status_alert.journey.ts b/x-pack/solutions/observability/plugins/synthetics/e2e/synthetics/journeys/alert_rules/custom_status_alert.journey.ts index 3d45e0698f616..531b306d31bab 100644 --- a/x-pack/solutions/observability/plugins/synthetics/e2e/synthetics/journeys/alert_rules/custom_status_alert.journey.ts +++ b/x-pack/solutions/observability/plugins/synthetics/e2e/synthetics/journeys/alert_rules/custom_status_alert.journey.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { journey, step, before, after } from '@elastic/synthetics'; +import { journey, step, before, after, expect } from '@elastic/synthetics'; import { syntheticsAppPageProvider } from '../../page_objects/synthetics_app'; import { SyntheticsServices } from '../services/synthetics_services'; @@ -51,10 +51,15 @@ journey(`CustomStatusAlert`, async ({ page, params }) => { await page.getByTestId('createNewStatusRule').click(); await page.getByTestId('ruleNameInput').fill('Synthetics status rule'); + let requestMade = false; + page.on('request', (request) => { + if (request.url().includes('api/alerting/rule') && request.method() === 'POST') { + requestMade = true; + } + }); await page.getByTestId('saveRuleButton').click(); await page.getByTestId('confirmModalConfirmButton').click(); - - await page.waitForSelector(`text='Created rule "Synthetics status rule"'`); + expect(requestMade).toBe(true); }); step('verify rule creation', async () => { diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/connector_description_popover.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/connector_description_popover.tsx index c49952e7b0b0c..101cc1493fb7f 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/connector_description_popover.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/components/connector_description_popover.tsx @@ -23,7 +23,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import connectorLogo from '../../../../../../assets/images/connector.svg'; +import { EuiIconPlugs } from '@kbn/search-shared-ui'; + import { KibanaLogic } from '../../../../../shared/kibana'; const nativePopoverPanels = [ @@ -40,7 +41,7 @@ const nativePopoverPanels = [ 'xpack.enterpriseSearch.connectorDescriptionPopover.connectorDescriptionBadge.native.configureConnectorLabel', { defaultMessage: 'Configure your connector using our Kibana UI' } ), - icons: [, ], + icons: [, ], id: 'native-configure-connector', }, ]; @@ -63,7 +64,7 @@ const connectorClientPopoverPanels = [ } ), icons: [ - , + , , , ], @@ -79,7 +80,7 @@ const connectorClientPopoverPanels = [ icons: [ , , - , + , , , ], diff --git a/x-pack/solutions/search/plugins/search_connectors/server/services/index.test.ts b/x-pack/solutions/search/plugins/search_connectors/server/services/index.test.ts index 40568158cea19..0f4196371c6d5 100644 --- a/x-pack/solutions/search/plugins/search_connectors/server/services/index.test.ts +++ b/x-pack/solutions/search/plugins/search_connectors/server/services/index.test.ts @@ -301,7 +301,7 @@ describe('AgentlessConnectorsInfraService', () => { expect(policies[1].agent_policy_ids).toBe(thirdPackagePolicy.policy_ids); }); - test('Skips policies that have missing fields', async () => { + test('Returns policies that have missing connector_id and connector_name but not service_type', async () => { const firstPackagePolicy = createPackagePolicyMock(); firstPackagePolicy.id = 'this-is-package-policy-id'; firstPackagePolicy.policy_ids = ['this-is-agent-policy-id']; @@ -325,13 +325,28 @@ describe('AgentlessConnectorsInfraService', () => { } as PackagePolicyInput, ]; + const thirdPackagePolicy = createPackagePolicyMock(); + thirdPackagePolicy.inputs = [ + { + type: 'connectors-py', + compiled_input: { + connector_id: '000002', + service_type: 'github', + }, + } as PackagePolicyInput, + ]; + packagePolicyService.fetchAllItems.mockResolvedValue( - getMockPolicyFetchAllItems([[firstPackagePolicy], [secondPackagePolicy]]) + getMockPolicyFetchAllItems([ + [firstPackagePolicy], + [secondPackagePolicy], + [thirdPackagePolicy], + ]) ); const policies = await service.getConnectorPackagePolicies(); - expect(policies.length).toBe(0); + expect(policies.length).toBe(2); }); }); describe('deployConnector', () => { @@ -579,6 +594,20 @@ describe('module', () => { is_deleted: false, }; + const confluenceConnector: ConnectorMetadata = { + id: '000004', + name: 'Confluence Connector', + service_type: 'confluence', + is_deleted: false, + }; + + const confluenceConnectorEmptySettings: ConnectorMetadata = { + id: '', + name: '', + service_type: 'confluence', + is_deleted: false, + }; + const deleted = (connector: ConnectorMetadata): ConnectorMetadata => { return { id: connector.id, @@ -606,6 +635,12 @@ describe('module', () => { connector_settings: mysqlConnector, }; + const confluencePackagePolicy: PackagePolicyMetadata = { + package_policy_id: '000004', + agent_policy_ids: [], + connector_settings: confluenceConnectorEmptySettings, + }; + describe('getPoliciesToDelete', () => { test('Returns one policy if connector has been soft-deleted', async () => { const policiesToDelete = getPoliciesToDelete( @@ -688,5 +723,14 @@ describe('module', () => { expect(missingConnectors).toContain(sharepointConnector); expect(missingConnectors).toContain(mysqlConnector); }); + + test('Returns none if Policy is created without a connector_id or connector_name', async () => { + const missingConnectors = getConnectorsToDeploy( + [githubPackagePolicy, sharepointPackagePolicy, mysqlPackagePolicy, confluencePackagePolicy], + [githubConnector, sharepointConnector, mysqlConnector, confluenceConnector] + ); + + expect(missingConnectors.length).toBe(0); + }); }); }); diff --git a/x-pack/solutions/search/plugins/search_connectors/server/services/index.ts b/x-pack/solutions/search/plugins/search_connectors/server/services/index.ts index f3640906ce985..1e961bb1c0e0c 100644 --- a/x-pack/solutions/search/plugins/search_connectors/server/services/index.ts +++ b/x-pack/solutions/search/plugins/search_connectors/server/services/index.ts @@ -106,8 +106,8 @@ export class AgentlessConnectorsInfraService { } if (input.compiled_input.connector_id == null) { - this.logger.debug(`Policy ${policy.id} is missing connector_id, skipping`); - continue; + this.logger.debug(`Policy ${policy.id} is missing connector_id`); + // No need to skip, that's fine } if (input.compiled_input.connector_name == null) { @@ -265,7 +265,11 @@ export const getConnectorsToDeploy = ( // If no package policies reference this connector by id then it should be deployed if ( - packagePolicies.every((packagePolicy) => packagePolicy.connector_settings.id !== connector.id) + packagePolicies.every( + (packagePolicy) => + connector.id !== packagePolicy.connector_settings.id && + connector.id !== packagePolicy.package_policy_id + ) ) { results.push(connector); } diff --git a/x-pack/solutions/search/plugins/search_indices/server/plugin.ts b/x-pack/solutions/search/plugins/search_indices/server/plugin.ts index f0adbb112356e..bff3d78aaab65 100644 --- a/x-pack/solutions/search/plugins/search_indices/server/plugin.ts +++ b/x-pack/solutions/search/plugins/search_indices/server/plugin.ts @@ -27,7 +27,6 @@ export class SearchIndicesPlugin public setup(core: CoreSetup) { this.logger.debug('searchIndices: Setup'); - this.logger.info('searchIndices test'); const router = core.http.createRouter(); // Register server side APIs diff --git a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx index ed9799abc2578..e96a7d3240aba 100644 --- a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx +++ b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.test.tsx @@ -18,11 +18,9 @@ jest.mock('../../hooks/use_kibana', () => ({ application: { navigateToUrl: jest.fn(), }, - share: { - url: { - locators: { - get: jest.fn().mockReturnValue(undefined), - }, + chrome: { + navLinks: { + get: jest.fn().mockReturnValue(undefined), }, }, }, @@ -38,13 +36,13 @@ const Wrapper: FC> = ({ children }) => { }; describe('CreateIndexButton', () => { - it('renders correctly when there is no locator', async () => { + it('renders correctly when there is no link to indices', async () => { const { queryByTestId } = render(, { wrapper: Wrapper }); expect(queryByTestId('createIndexButton')).not.toBeInTheDocument(); }); - it('renders correctly when there is a locator', async () => { + it('renders correctly when navlink exists', async () => { const navigateToUrl = jest.fn(); (useKibana as unknown as jest.Mock).mockImplementation(() => ({ @@ -52,14 +50,11 @@ describe('CreateIndexButton', () => { application: { navigateToUrl, }, - share: { - url: { - locators: { - get: jest.fn().mockReturnValue({ - getUrl: jest.fn().mockReturnValue('mock-url'), - getRedirectUrl: jest.fn().mockReturnValue('mock-shown-url'), - }), - }, + chrome: { + navLinks: { + get: jest.fn().mockReturnValue({ + url: 'mock-url', + }), }, }, }, diff --git a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx index 0d12e58c22eed..ad30d0a0edc8f 100644 --- a/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx +++ b/x-pack/solutions/search/plugins/search_playground/public/components/setup_page/create_index_button.tsx @@ -5,44 +5,43 @@ * 2.0. */ +import React, { useCallback } from 'react'; import { EuiButton, EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useCallback, useMemo } from 'react'; +import { SEARCH_INDICES, SEARCH_INDICES_CREATE_INDEX } from '@kbn/deeplinks-search/constants'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../../hooks/use_kibana'; export const CreateIndexButton: React.FC = () => { const { - services: { application, share }, + services: { application, chrome }, } = useKibana(); - const createIndexLocator = useMemo( - () => share.url.locators.get('SEARCH_CREATE_INDEX'), - [share.url.locators] - ); + const createIndexUrl = chrome.navLinks.get( + `${SEARCH_INDICES}:${SEARCH_INDICES_CREATE_INDEX}` + )?.url; const handleCreateIndexClick = useCallback( - async (event: React.MouseEvent) => { + (event: React.MouseEvent) => { event.preventDefault(); - if (!createIndexLocator) { + if (!createIndexUrl) { return; } - const url = await createIndexLocator.getUrl({}); - application?.navigateToUrl(url); + application?.navigateToUrl(createIndexUrl); }, - [application, createIndexLocator] + [application, createIndexUrl] ); - return createIndexLocator ? ( + return createIndexUrl ? ( // eslint-disable-next-line @elastic/eui/href-or-on-click void | null; } -const panelCss = css` - margin: ${euiThemeVars.euiSizeL} 0; - padding: ${euiThemeVars.euiSizeL} 0; -`; const EmptyViewerStateComponent: FC = ({ title, body, @@ -47,6 +42,10 @@ const EmptyViewerStateComponent: FC = ({ onEmptyButtonStateClick, }) => { const { euiTheme } = useEuiTheme(); + const panelCss = css` + margin: ${euiTheme.size.l} 0; + padding: ${euiTheme.size.l} 0; + `; const euiEmptyPromptProps = useMemo(() => { switch (viewerStatus) { diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx deleted file mode 100644 index d0278771b249d..0000000000000 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/conditions.styles.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; - -// TODO check font Roboto Mono -export const nestedGroupSpaceCss = css` - margin-left: ${euiThemeVars.euiSizeXL}; - margin-bottom: ${euiThemeVars.euiSizeXS}; - padding-top: ${euiThemeVars.euiSizeXS}; -`; - -export const valueContainerCss = css` - display: flex; - align-items: center; - margin-left: ${euiThemeVars.euiSizeS}; -`; -export const expressionContainerCss = css` - display: flex; - align-items: center; -`; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/__snapshots__/entry_content.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/__snapshots__/entry_content.test.tsx.snap index f814be75fa63e..a24b783532c2e 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/__snapshots__/entry_content.test.tsx.snap +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/__snapshots__/entry_content.test.tsx.snap @@ -6,49 +6,45 @@ exports[`EntryContent should render a nested value 1`] = ` data-test-subj="EntryContentlistlist_id0EntryContent" >
+ + +
+ + -
- - - + included in + - - included in - - - - list_id - + list_id -
+
diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx index 7f75694b16d34..5b0a4b2092ea9 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/conditions/entry_content/index.tsx @@ -6,13 +6,9 @@ */ import React, { ElementType, FC, memo } from 'react'; -import { EuiExpression, EuiToken, EuiFlexGroup } from '@elastic/eui'; +import { EuiExpression, EuiToken, EuiFlexGroup, useEuiTheme, EuiFlexItem } from '@elastic/eui'; +import { css } from '@emotion/react'; import { ListOperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; -import { - nestedGroupSpaceCss, - valueContainerCss, - expressionContainerCss, -} from '../conditions.styles'; import type { Entry } from '../types'; import * as i18n from '../../translations'; import { getValue, getValueExpression } from './entry_content.helper'; @@ -31,49 +27,49 @@ export const EntryContent: FC = memo( const value = getValue(entry); const operator = 'operator' in entry ? entry.operator : ''; + const { euiTheme } = useEuiTheme(); + const nestedGroupSpaceStyles = css` + margin-left: ${euiTheme.size.l}; + margin-bottom: ${euiTheme.size.xs}; + padding-top: ${euiTheme.size.xs}; + `; + const valueContainerStyles = css` + flex-direction: row; + `; + const entryKey = `${field}${type}${value}${index}`; return (
-
- {isNestedEntry ? ( - - - -
- - {getValueExpression( - type as ListOperatorTypeEnum, - operator, - value, - showValueListModal - )} -
-
- ) : ( - <> - - + {isNestedEntry ? ( + + + + {getValueExpression( type as ListOperatorTypeEnum, operator, value, showValueListModal )} - - )} -
+ + + ) : ( + <> + + {getValueExpression(type as ListOperatorTypeEnum, operator, value, showValueListModal)} + + )}
); } diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx index 209ce3b59553c..89f94e60d3c64 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/details_info/index.tsx @@ -6,9 +6,8 @@ */ import React, { memo } from 'react'; -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; import * as i18n from '../../translations'; interface MetaInfoDetailsProps { @@ -18,11 +17,13 @@ interface MetaInfoDetailsProps { dataTestSubj?: string; } -const euiBadgeFontFamily = css` - font-family: ${euiThemeVars.euiFontFamily}; -`; export const MetaInfoDetails = memo( ({ label, lastUpdate, lastUpdateValue, dataTestSubj }) => { + const { euiTheme } = useEuiTheme(); + const euiBadgeFontFamily = css` + font-family: ${euiTheme.font.family}; + `; + return ( { exceptionsUtilityComponent={() => null} getFormattedComments={() => []} showValueListModal={MockedShowValueListModal} - /> + />, + { wrapper: EuiThemeProvider } ); expect(wrapper.getByTestId('exceptionsContainer')).toBeInTheDocument(); fireEvent.click(wrapper.getByTestId('exceptionItemCardMetaInfoEmptyButton')); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx index 6fde321cfcb93..f44fc8db1a1e2 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/exception_items/index.tsx @@ -6,7 +6,6 @@ */ import React, { ElementType } from 'react'; -import { css } from '@emotion/react'; import type { FC } from 'react'; import { EuiCommentProps, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; @@ -16,7 +15,6 @@ import type { ExceptionListTypeEnum, } from '@kbn/securitysolution-io-ts-list-types'; -import { euiThemeVars } from '@kbn/ui-theme'; import { EmptyViewerState, ExceptionItemCard, Pagination, PaginationProps } from '../..'; import type { @@ -26,13 +24,6 @@ import type { GetExceptionItemProps, } from '../types'; -const exceptionItemCss = css` - margin: ${euiThemeVars.euiSize} 0; - &div:first-child { - margin: ${euiThemeVars.euiSizeXS} 0 ${euiThemeVars.euiSize}; - } -`; - interface ExceptionItemsProps { lastUpdated: string | number | null; viewerStatus: ViewerStatus; @@ -98,13 +89,12 @@ const ExceptionItemsComponent: FC = ({ return ( <> - + {exceptions.map((exception) => (
1 1`] = `
{ }) as ReactElement[]; result.forEach((link) => { - const wrapper = render(link); + const wrapper = render(link, { + wrapper: EuiThemeProvider, + }); expect(wrapper.container).toMatchSnapshot(); expect(wrapper.getByTestId('generateLinedRulesMenuItemsTestActionItem1a2b3c')); expect(wrapper.getByTestId('generateLinedRulesMenuItemsTestLeftIcon')); @@ -52,7 +55,7 @@ describe('generateLinedRulesMenuItems', () => { it('should render the second linked rule and apply the css when the length is > 1', () => { const result: ReactElement[] = getSecurityLinkAction(dataTestSubj); - const wrapper = render(result[1]); + const wrapper = render(result[1], { wrapper: EuiThemeProvider }); expect(wrapper.container).toMatchSnapshot(); expect(wrapper.getByTestId('generateLinedRulesMenuItemsTestActionItem2a2b3c')); }); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx index 9187d0975e92a..efeb68a6edfe6 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/index.tsx @@ -6,9 +6,9 @@ */ import React, { ElementType, ReactElement } from 'react'; +import styled from '@emotion/styled'; import { EuiContextMenuItem, EuiFlexGroup, EuiFlexItem, EuiIcon, IconType } from '@elastic/eui'; import { Rule } from '../types'; -import { itemContentCss, containerCss } from './menu_link.styles'; interface MenuItemLinkedRulesProps { leftIcon?: IconType; @@ -28,22 +28,28 @@ export const generateLinkedRulesMenuItems = ({ const SecurityLinkAnchor = securityLinkAnchorComponent; return linkedRules.map((rule) => { return ( - 1 ? containerCss : ''} + - + {leftIcon ? ( ) : null} - + - + ); }); }; + +const LinkedRulesMenuItem = styled(EuiContextMenuItem)` + &:not(:last-child) { + border-bottom: ${({ theme }) => theme.euiTheme.border.thin}; + } + color: ${({ theme }) => theme.euiTheme.colors.textPrimary}; +`; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts deleted file mode 100644 index 886369b22e23b..0000000000000 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/generate_linked_rules_menu_item/menu_link.styles.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; - -export const containerCss = css` - border-bottom: 1px solid ${euiThemeVars.euiColorLightShade}; -`; - -export const itemContentCss = css` - color: ${euiThemeVars.euiColorPrimary}; - flex-basis: content; -`; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/header_menu.test.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/header_menu.test.tsx index 2869b16e3aaa2..5ddac177a76fd 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/header_menu.test.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/header_menu/header_menu.test.tsx @@ -6,6 +6,7 @@ */ import { createEvent, fireEvent, render } from '@testing-library/react'; +import { EuiThemeProvider } from '@elastic/eui'; import React from 'react'; import { HeaderMenu } from '.'; import { actions, actionsWithDisabledDelete } from '../mocks/header.mock'; @@ -124,7 +125,9 @@ describe('HeaderMenu', () => { emptyButton actions={customActions} useCustomActions - /> + />, + + { wrapper: EuiThemeProvider } ); expect(wrapper.container).toMatchSnapshot(); diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap index 1fcea8da393df..0b11db767d13f 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap @@ -53,10 +53,10 @@ exports[`ExceptionListHeader should render edit modal 1`] = `

-
-
List Name -
-
+ -
-
+ +

-

-
-
List description -
-
+ -
-
-
+ + -
List ID : -
-
+ List_Id -
-
-
+ + +

@@ -273,10 +273,10 @@ exports[`ExceptionListHeader should render the List Header with name, default de

-
-
List Name -
-
+ -
-
+ +

-

-
-
Add a description -
-
+ -
-
-
+ + -
List ID : -
-
+ List_Id -
-
-
+ + +

@@ -493,10 +493,10 @@ exports[`ExceptionListHeader should render the List Header with name, default de

-
-
List Name -
-
+ -
+

-

-
-
Add a description -
-
+ -
-
+ -
List ID : -
-
+ List_Id -
-
-
+ + +

@@ -686,10 +686,10 @@ exports[`ExceptionListHeader should render the List Header with name, default de

-
-
List Name -
-
+ -
+

-

-
-
Add a description -
-
+ -
-
+ -
List ID : -
-
+ List_Id -
-
-
+ + +

diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx index a50c0f17e449d..5b688999f2372 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/list_header/index.tsx @@ -10,7 +10,14 @@ import React from 'react'; import { css } from '@emotion/react'; import type { FC } from 'react'; -import { EuiIcon, EuiPageHeader, EuiText, useEuiFontSize, useEuiTheme } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiIcon, + EuiPageHeader, + EuiText, + useEuiFontSize, + useEuiTheme, +} from '@elastic/eui'; import * as i18n from '../translations'; import { MenuItems } from './menu_items'; import { TextWithEdit } from '../text_with_edit'; @@ -76,7 +83,8 @@ const ExceptionListHeaderComponent: FC = ({ `; const descriptionContainerStyles = css` // negates the static EuiSpacer when using Title + Description in PageHeader - margin-top: -${euiTheme.size.l}; + margin-top: -${euiTheme.size.m}; + margin-bottom: ${euiTheme.size.s}; `; return ( @@ -95,7 +103,12 @@ const ExceptionListHeaderComponent: FC = ({ responsive data-test-subj={`${dataTestSubj || ''}PageHeader`} description={ -
+ = ({ text={listDetails.description || i18n.EXCEPTION_LIST_HEADER_DESCRIPTION} onEdit={onEdit} /> -
- {i18n.EXCEPTION_LIST_HEADER_LIST_ID}: - {listId} -
-
+ + + {i18n.EXCEPTION_LIST_HEADER_LIST_ID}: + + + {listId} + + + } rightSideItems={[ -
-
Test -
-
+ -
+
`; exports[`TextWithEdit should render the edit icon when isReadonly is false 1`] = `
-
-
Test -
-
+ -
-
+ +
`; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx index 1428dcfa238ea..07f5367bf8256 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/text_with_edit/index.tsx @@ -8,7 +8,7 @@ import React, { FC } from 'react'; import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Interpolation, Theme } from '@emotion/react'; -import { textWithEditContainerCss, editIconCss } from './text_with_edit.styles'; +import { textWithEditContainerCss } from './text_with_edit.styles'; interface TextWithEditProps { isReadonly: boolean; dataTestSubj?: string; @@ -25,13 +25,13 @@ const TextWithEditComponent: FC = ({ textCss, }) => { return ( - - + + {text} - + {isReadonly ? null : ( = ({ value, tooltipIconType = 'iInCircle', @@ -33,14 +28,12 @@ export const ValueWithSpaceWarning: FC = ({ }); if (!showSpaceWarningIcon || !value) return null; return ( -
- - - -
+ + + ); }; diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/tsconfig.json b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/tsconfig.json index 5378a8fbdfe1a..03ab9b9afec10 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/tsconfig.json +++ b/x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/tsconfig.json @@ -6,7 +6,8 @@ "jest", "node", "react", - "@emotion/react/types/css-prop" + "@emotion/react/types/css-prop", + "../../../../../typings/emotion.d.ts" ] }, "include": [ @@ -17,7 +18,6 @@ "kbn_references": [ "@kbn/securitysolution-io-ts-list-types", "@kbn/securitysolution-autocomplete", - "@kbn/ui-theme", "@kbn/i18n", "@kbn/i18n-react", ], diff --git a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts index 547b45969be71..b4a2e3d44ef06 100644 --- a/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts +++ b/x-pack/solutions/security/packages/kbn-securitysolution-io-ts-list-types/src/common/meta/index.ts @@ -7,7 +7,7 @@ import * as t from 'io-ts'; -export const meta = t.object; +export const meta = t.UnknownRecord; export type Meta = t.TypeOf; export const metaOrUndefined = t.union([meta, t.undefined]); export type MetaOrUndefined = t.TypeOf; diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx index e0e0690cf7edf..5074af8b5a2a7 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credentials_form/gcp_credential_form.tsx @@ -338,17 +338,16 @@ export const getInputVarsFields = (input: NewPackagePolicyInput, fields: GcpFiel }); const getSetupFormatFromInput = ( - input: Extract< - NewPackagePolicyPostureInput, - { type: 'cloudbeat/cis_aws' | 'cloudbeat/cis_eks' | 'cloudbeat/cis_gcp' } - > + input: Extract ): SetupFormatGCP => { - const credentialsType = input.streams[0].vars?.setup_access?.value; + const credentialsType = getGcpCredentialsType(input); + // Google Cloud shell is the default value if (!credentialsType) { return GCP_SETUP_ACCESS.CLOUD_SHELL; } - if (credentialsType !== GCP_SETUP_ACCESS.CLOUD_SHELL) { + + if (credentialsType !== GCP_CREDENTIALS_TYPE.CREDENTIALS_NONE) { return GCP_SETUP_ACCESS.MANUAL; } @@ -474,10 +473,6 @@ export const GcpCredentialsForm = ({ updatePolicy( getPosturePolicy(newPolicy, input.type, { - setup_access: { - value: GCP_SETUP_ACCESS.CLOUD_SHELL, - type: 'text', - }, 'gcp.credentials.type': { value: GCP_CREDENTIALS_TYPE.CREDENTIALS_NONE, type: 'text', @@ -490,10 +485,6 @@ export const GcpCredentialsForm = ({ } else { updatePolicy( getPosturePolicy(newPolicy, input.type, { - setup_access: { - value: GCP_SETUP_ACCESS.MANUAL, - type: 'text', - }, 'gcp.credentials.type': { // Restoring last manual credentials type value: lastCredentialsType.current || GCP_CREDENTIALS_TYPE.CREDENTIALS_FILE, diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index 676d75b4a9088..64019ad2690c6 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -1169,7 +1169,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_type: { value: 'credentials-file' }, - setup_access: { value: 'manual' }, }); const { getByText } = render( @@ -1191,7 +1190,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { credentials_type: { value: 'credentials-file' }, - setup_access: { value: 'manual' }, }); const { getByText } = render( @@ -1208,7 +1206,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.account_type': { value: GCP_ORGANIZATION_ACCOUNT }, - setup_access: { value: 'google_cloud_shell' }, }); const { getByTestId } = render( @@ -1228,7 +1225,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.credentials.type': { value: 'credentials-file' }, - setup_access: { value: 'manual' }, }); const { getByLabelText, getByRole } = render( @@ -1247,7 +1243,6 @@ describe('', () => { policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.project_id': { value: 'a' }, 'gcp.credentials.type': { value: 'credentials-file' }, - setup_access: { value: 'manual' }, }); const { getByTestId } = render( @@ -1289,7 +1284,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.account_type': { value: GCP_ORGANIZATION_ACCOUNT }, - setup_access: { value: 'google_cloud_shell' }, }); const { getByLabelText, getByTestId } = render( @@ -1305,7 +1299,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.account_type': { value: GCP_ORGANIZATION_ACCOUNT }, - setup_access: { value: 'manual' }, }); const { getByLabelText, getByTestId } = render( @@ -1321,7 +1314,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.account_type': { value: GCP_SINGLE_ACCOUNT }, - setup_access: { value: 'google_cloud_shell' }, }); const { queryByLabelText, queryByTestId } = render( @@ -1337,7 +1329,6 @@ describe('', () => { let policy = getMockPolicyGCP(); policy = getPosturePolicy(policy, CLOUDBEAT_GCP, { 'gcp.account_type': { value: GCP_ORGANIZATION_ACCOUNT }, - setup_access: { value: 'manual' }, }); const { getByTestId } = render( diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts index 13bb5398de44c..4d25816b4e149 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts @@ -446,7 +446,6 @@ describe('getDefaultGcpHiddenVars', () => { expect(result).toMatchObject({ 'gcp.credentials.type': { value: 'credentials-json', type: 'text' }, - setup_access: { value: 'manual', type: 'text' }, }); }); @@ -456,7 +455,6 @@ describe('getDefaultGcpHiddenVars', () => { expect(result).toMatchObject({ 'gcp.credentials.type': { value: 'credentials-none', type: 'text' }, - setup_access: { value: 'google_cloud_shell', type: 'text' }, }); }); @@ -483,7 +481,6 @@ describe('getDefaultGcpHiddenVars', () => { expect(result).toMatchObject({ 'gcp.credentials.type': { value: 'credentials-file', type: 'text' }, - setup_access: { value: 'manual', type: 'text' }, }); }); }); diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index 0a0bdddf731a4..e384a7fc49d64 100644 --- a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -43,7 +43,7 @@ import { DEFAULT_AWS_CREDENTIALS_TYPE, DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE, } from './aws_credentials_form/get_aws_credentials_form_options'; -import { GCP_CREDENTIALS_TYPE, GCP_SETUP_ACCESS } from './gcp_credentials_form/gcp_credential_form'; +import { GCP_CREDENTIALS_TYPE } from './gcp_credentials_form/gcp_credential_form'; import { AZURE_CREDENTIALS_TYPE } from './azure_credentials_form/azure_credentials_form'; // Posture policies only support the default namespace @@ -257,10 +257,6 @@ export const getDefaultGcpHiddenVars = ( value: GCP_CREDENTIALS_TYPE.CREDENTIALS_JSON, type: 'text', }, - setup_access: { - value: GCP_SETUP_ACCESS.MANUAL, - type: 'text', - }, }; } @@ -271,10 +267,6 @@ export const getDefaultGcpHiddenVars = ( value: GCP_CREDENTIALS_TYPE.CREDENTIALS_NONE, type: 'text', }, - setup_access: { - value: GCP_SETUP_ACCESS.CLOUD_SHELL, - type: 'text', - }, }; } @@ -283,10 +275,6 @@ export const getDefaultGcpHiddenVars = ( value: GCP_CREDENTIALS_TYPE.CREDENTIALS_FILE, type: 'text', }, - setup_access: { - value: GCP_SETUP_ACCESS.MANUAL, - type: 'text', - }, }; }; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/__mocks__/request_context.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/__mocks__/request_context.ts index 4c80585d13a70..8866e23a9816b 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/__mocks__/request_context.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/__mocks__/request_context.ts @@ -7,6 +7,7 @@ import { coreMock, loggingSystemMock } from '@kbn/core/server/mocks'; import { licensingMock } from '@kbn/licensing-plugin/server/mocks'; import { actionsClientMock } from '@kbn/actions-plugin/server/actions_client/actions_client.mock'; +import { inferenceMock } from '@kbn/inference-plugin/server/mocks'; import { MockedKeys } from '@kbn/utility-types-jest'; import { AwaitedProperties } from '@kbn/utility-types'; import { @@ -145,7 +146,7 @@ const createElasticAssistantRequestContextMock = ( getCurrentUser: jest.fn().mockReturnValue(authenticatedUser), getServerBasePath: jest.fn(), getSpaceId: jest.fn().mockReturnValue('default'), - inference: { getClient: jest.fn() }, + inference: inferenceMock.createStartContract(), llmTasks: { retrieveDocumentationAvailable: jest.fn(), retrieveDocumentation: jest.fn() }, core: clients.core, savedObjectsClient: clients.elasticAssistant.savedObjectsClient, diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.test.tsx b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.test.tsx index d38bcfef0e755..5145efdbec8f2 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.test.tsx +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.test.tsx @@ -20,7 +20,7 @@ import { EsqlContentReference, IndexEntry, } from '@kbn/elastic-assistant-common'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; // Mock dependencies jest.mock('@elastic/elasticsearch'); @@ -149,7 +149,7 @@ describe('getStructuredToolForIndexEntry', () => { const mockEsClient = {} as ElasticsearchClient; const mockIndexEntry = getCreateKnowledgeBaseEntrySchemaMock({ type: 'index' }) as IndexEntry; - const contentReferencesStore = contentReferencesStoreFactoryMock(); + const contentReferencesStore = newContentReferencesStoreMock(); it('should return a DynamicStructuredTool with correct name and schema', () => { const tool = getStructuredToolForIndexEntry({ diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts index 7d612cd53ae74..174fd1c6d923b 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/helpers.ts @@ -148,7 +148,7 @@ export const getStructuredToolForIndexEntry = ({ }: { indexEntry: IndexEntry; esClient: ElasticsearchClient; - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; logger: Logger; }): DynamicStructuredTool => { const inputSchema = indexEntry.inputSchema?.reduce((prev, input) => { diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.test.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.test.ts index 6357224cc7d85..b21ac92170e73 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.test.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.test.ts @@ -27,7 +27,7 @@ import { getSecurityLabsDocsCount, } from '../../lib/langchain/content_loaders/security_labs_loader'; import { DynamicStructuredTool } from '@langchain/core/tools'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; jest.mock('../../lib/langchain/content_loaders/security_labs_loader'); jest.mock('p-retry'); const date = '2023-03-28T22:27:28.159Z'; @@ -522,7 +522,7 @@ describe('AIAssistantKnowledgeBaseDataClient', () => { const result = await client.getAssistantTools({ esClient: esClientMock, - contentReferencesStore: contentReferencesStoreFactoryMock(), + contentReferencesStore: newContentReferencesStoreMock(), }); expect(result).toHaveLength(1); @@ -537,7 +537,7 @@ describe('AIAssistantKnowledgeBaseDataClient', () => { const result = await client.getAssistantTools({ esClient: esClientMock, - contentReferencesStore: contentReferencesStoreFactoryMock(), + contentReferencesStore: newContentReferencesStoreMock(), }); expect(result).toEqual([]); @@ -550,7 +550,7 @@ describe('AIAssistantKnowledgeBaseDataClient', () => { const result = await client.getAssistantTools({ esClient: esClientMock, - contentReferencesStore: contentReferencesStoreFactoryMock(), + contentReferencesStore: newContentReferencesStoreMock(), }); expect(result).toEqual([]); diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts index 11edd3ad0bf02..561467f2256ea 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/ai_assistant_data_clients/knowledge_base/index.ts @@ -817,7 +817,7 @@ export class AIAssistantKnowledgeBaseDataClient extends AIAssistantDataClient { contentReferencesStore, esClient, }: { - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; esClient: ElasticsearchClient; }): Promise => { const user = this.options.currentUser; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/executors/types.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/executors/types.ts index 0ccc7b6453684..e1133aa0d9bb0 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/executors/types.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/executors/types.ts @@ -49,7 +49,7 @@ export interface AgentExecutorParams { assistantTools?: AssistantTool[]; connectorId: string; conversationId?: string; - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; dataClients?: AssistantDataClients; esClient: ElasticsearchClient; langChainMessages: BaseMessage[]; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.test.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.test.ts index 9bd6730417b1d..5bcf61ec07a08 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.test.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.test.ts @@ -12,15 +12,19 @@ import { invokeGraph, streamGraph } from './helpers'; import { loggerMock } from '@kbn/logging-mocks'; import { AgentExecutorParams, AssistantDataClients } from '../../executors/types'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { getPrompt, resolveProviderAndModel } from '@kbn/security-ai-prompts'; import { getFindAnonymizationFieldsResultWithSingleHit } from '../../../../__mocks__/response'; import { createOpenAIToolsAgent, createStructuredChatAgent, createToolCallingAgent, } from 'langchain/agents'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; -import { resolveProviderAndModel } from '@kbn/security-ai-prompts'; +import { AssistantTool, AssistantToolParams } from '../../../..'; +import { promptGroupId as toolsGroupId } from '../../../prompt/tool_prompts'; +import { promptDictionary } from '../../../prompt'; +import { promptGroupId } from '../../../prompt/local_prompt_object'; jest.mock('./graph'); jest.mock('./helpers'); jest.mock('langchain/agents'); @@ -29,6 +33,7 @@ jest.mock('@kbn/langchain/server/tracers/telemetry'); jest.mock('@kbn/security-ai-prompts'); const getDefaultAssistantGraphMock = getDefaultAssistantGraph as jest.Mock; const resolveProviderAndModelMock = resolveProviderAndModel as jest.Mock; +const getPromptMock = getPrompt as jest.Mock; describe('callAssistantGraph', () => { const mockDataClients = { anonymizationFieldsDataClient: { @@ -79,7 +84,7 @@ describe('callAssistantGraph', () => { telemetryParams: {}, traceOptions: {}, responseLanguage: 'English', - contentReferencesStore: contentReferencesStoreFactoryMock(), + contentReferencesStore: newContentReferencesStoreMock(), } as unknown as AgentExecutorParams; beforeEach(() => { @@ -98,6 +103,7 @@ describe('callAssistantGraph', () => { (mockDataClients?.anonymizationFieldsDataClient?.findDocuments as jest.Mock).mockResolvedValue( getFindAnonymizationFieldsResultWithSingleHit() ); + getPromptMock.mockResolvedValue('prompt'); }); it('calls invokeGraph with correct parameters for non-streaming', async () => { @@ -173,6 +179,58 @@ describe('callAssistantGraph', () => { }); }); + it('calls getPrompt for each tool and the default system prompt', async () => { + const getTool = jest.fn(); + const mockTool: AssistantTool = { + id: 'id', + name: 'name', + description: 'description', + sourceRegister: 'sourceRegister', + isSupported: (params: AssistantToolParams) => true, + getTool, + }; + const params = { + ...defaultParams, + assistantTools: [ + { ...mockTool, name: 'test-tool' }, + { ...mockTool, name: 'test-tool2' }, + ], + }; + await callAssistantGraph(params); + + expect(getPromptMock).toHaveBeenCalledTimes(3); + expect(getPromptMock).toHaveBeenCalledWith( + expect.objectContaining({ + model: 'test-model', + provider: 'openai', + promptId: 'test-tool', + promptGroupId: toolsGroupId, + }) + ); + expect(getPromptMock).toHaveBeenCalledWith( + expect.objectContaining({ + model: 'test-model', + provider: 'openai', + promptId: 'test-tool2', + promptGroupId: toolsGroupId, + }) + ); + expect(getPromptMock).toHaveBeenCalledWith( + expect.objectContaining({ + model: 'test-model', + provider: 'openai', + promptId: promptDictionary.systemPrompt, + promptGroupId: promptGroupId.aiAssistant, + }) + ); + + expect(getTool).toHaveBeenCalledWith( + expect.objectContaining({ + description: 'prompt', + }) + ); + }); + describe('agentRunnable', () => { it('creates OpenAIToolsAgent for openai llmType', async () => { const params = { ...defaultParams, llmType: 'openai' }; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts index c2997cc623147..22889419885ec 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/index.ts @@ -15,10 +15,11 @@ import { import { APMTracer } from '@kbn/langchain/server/tracers/apm'; import { TelemetryTracer } from '@kbn/langchain/server/tracers/telemetry'; import { pruneContentReferences, MessageMetadata } from '@kbn/elastic-assistant-common'; -import { resolveProviderAndModel } from '@kbn/security-ai-prompts'; +import { getPrompt, resolveProviderAndModel } from '@kbn/security-ai-prompts'; +import { localToolPrompts, promptGroupId as toolsGroupId } from '../../../prompt/tool_prompts'; import { promptGroupId } from '../../../prompt/local_prompt_object'; import { getModelOrOss } from '../../../prompt/helpers'; -import { getPrompt, promptDictionary } from '../../../prompt'; +import { getPrompt as localGetPrompt, promptDictionary } from '../../../prompt'; import { getLlmClass } from '../../../../routes/utils'; import { EsAnonymizationFieldsSchema } from '../../../../ai_assistant_data_clients/anonymization_fields/types'; import { AssistantToolParams } from '../../../../types'; @@ -124,9 +125,33 @@ export const callAssistantGraph: AgentExecutor = async ({ telemetry, }; - const tools: StructuredTool[] = assistantTools.flatMap( - (tool) => tool.getTool({ ...assistantToolParams, llm: createLlmInstance(), isOssModel }) ?? [] - ); + const tools: StructuredTool[] = ( + await Promise.all( + assistantTools.map(async (tool) => { + let description: string | undefined; + try { + description = await getPrompt({ + actionsClient, + connectorId, + localPrompts: localToolPrompts, + model: getModelOrOss(llmType, isOssModel, request.body.model), + promptId: tool.name, + promptGroupId: toolsGroupId, + provider: llmType, + savedObjectsClient, + }); + } catch (e) { + logger.error(`Failed to get prompt for tool: ${tool.name}`); + } + return tool.getTool({ + ...assistantToolParams, + llm: createLlmInstance(), + isOssModel, + description, + }); + }) + ) + ).filter((e) => e != null) as StructuredTool[]; // If KB enabled, fetch for any KB IndexEntries and generate a tool for each if (isEnabledKnowledgeBase) { @@ -139,7 +164,7 @@ export const callAssistantGraph: AgentExecutor = async ({ } } - const defaultSystemPrompt = await getPrompt({ + const defaultSystemPrompt = await localGetPrompt({ actionsClient, connectorId, model: getModelOrOss(llmType, isOssModel, request.body.model), @@ -176,7 +201,7 @@ export const callAssistantGraph: AgentExecutor = async ({ const telemetryTracer = telemetryParams ? new TelemetryTracer( { - elasticTools: assistantTools.map(({ name }) => name), + elasticTools: tools.map(({ name }) => name), totalTools: tools.length, telemetry, telemetryParams, diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts index b01f9d3fabe9f..3cdddef18ea87 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/langchain/graphs/default_assistant_graph/nodes/generate_chat_title.ts @@ -8,45 +8,22 @@ import { StringOutputParser } from '@langchain/core/output_parsers'; import { ChatPromptTemplate } from '@langchain/core/prompts'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; +import { getPrompt, promptDictionary } from '../../../../prompt'; import { AgentState, NodeParamsBase } from '../types'; import { NodeType } from '../constants'; +import { promptGroupId } from '../../../../prompt/local_prompt_object'; -export const GENERATE_CHAT_TITLE_PROMPT = (responseLanguage: string, llmType?: string) => - llmType === 'bedrock' - ? ChatPromptTemplate.fromMessages([ - [ - 'system', - `You are a helpful assistant for Elastic Security. Assume the following user message is the start of a conversation between you and a user; give this conversation a title based on the content below. DO NOT UNDER ANY CIRCUMSTANCES wrap this title in single or double quotes. This title is shown in a list of conversations to the user, so title it for the user, not for you. Please create the title in ${responseLanguage}. Respond with the title only with no other text explaining your response. As an example, for the given MESSAGE, this is the TITLE: - - MESSAGE: I am having trouble with the Elastic Security app. - TITLE: Troubleshooting Elastic Security app issues - `, - ], - ['human', '{input}'], - ]) - : llmType === 'gemini' - ? ChatPromptTemplate.fromMessages([ - [ - 'system', - `You are a title generator for a helpful assistant for Elastic Security. Assume the following human message is the start of a conversation between you and a human. Generate a relevant conversation title for the human's message in plain text. Make sure the title is formatted for the user, without using quotes or markdown. The title should clearly reflect the content of the message and be appropriate for a list of conversations. Please create the title in ${responseLanguage}. Respond only with the title. As an example, for the given MESSAGE, this is the TITLE: - - MESSAGE: I am having trouble with the Elastic Security app. - TITLE: Troubleshooting Elastic Security app issues - `, - ], - ['human', '{input}'], - ]) - : ChatPromptTemplate.fromMessages([ - [ - 'system', - `You are a helpful assistant for Elastic Security. Assume the following user message is the start of a conversation between you and a user; give this conversation a title based on the content below. DO NOT UNDER ANY CIRCUMSTANCES wrap this title in single or double quotes. This title is shown in a list of conversations to the user, so title it for the user, not for you. Please create the title in ${responseLanguage}. As an example, for the given MESSAGE, this is the TITLE: - - MESSAGE: I am having trouble with the Elastic Security app. - TITLE: Troubleshooting Elastic Security app issues - `, - ], - ['human', '{input}'], - ]); +export const GENERATE_CHAT_TITLE_PROMPT = ({ + prompt, + responseLanguage, +}: { + prompt: string; + responseLanguage: string; +}) => + ChatPromptTemplate.fromMessages([ + ['system', `${prompt}\nPlease create the title in ${responseLanguage}.`], + ['human', '{input}'], + ]); export interface GenerateChatTitleParams extends NodeParamsBase { state: AgentState; @@ -54,7 +31,9 @@ export interface GenerateChatTitleParams extends NodeParamsBase { } export async function generateChatTitle({ + actionsClient, logger, + savedObjectsClient, state, model, }: GenerateChatTitleParams): Promise> { @@ -64,7 +43,15 @@ export async function generateChatTitle({ ); const outputParser = new StringOutputParser(); - const graph = GENERATE_CHAT_TITLE_PROMPT(state.responseLanguage, state.llmType) + const prompt = await getPrompt({ + actionsClient, + connectorId: state.connectorId, + promptId: promptDictionary.chatTitle, + promptGroupId: promptGroupId.aiAssistant, + provider: state.llmType, + savedObjectsClient, + }); + const graph = GENERATE_CHAT_TITLE_PROMPT({ prompt, responseLanguage: state.responseLanguage }) .pipe(model) .pipe(outputParser); diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/local_prompt_object.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/local_prompt_object.ts index e5728df0d7058..6dd9a2088f93b 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/local_prompt_object.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/local_prompt_object.ts @@ -21,6 +21,9 @@ import { GEMINI_SYSTEM_PROMPT, GEMINI_USER_PROMPT, STRUCTURED_SYSTEM_PROMPT, + BEDROCK_CHAT_TITLE, + GEMINI_CHAT_TITLE, + DEFAULT_CHAT_TITLE, } from './prompts'; export const promptGroupId = { @@ -31,6 +34,7 @@ export const promptGroupId = { export const promptDictionary = { systemPrompt: `systemPrompt`, userPrompt: `userPrompt`, + chatTitle: `chatTitle`, attackDiscoveryDefault: `default`, attackDiscoveryRefine: `refine`, attackDiscoveryContinue: `continue`, @@ -154,4 +158,27 @@ export const localPrompts: Prompt[] = [ default: ATTACK_DISCOVERY_GENERATION_INSIGHTS, }, }, + { + promptId: promptDictionary.chatTitle, + promptGroupId: promptGroupId.aiAssistant, + prompt: { + default: DEFAULT_CHAT_TITLE, + }, + }, + { + promptId: promptDictionary.chatTitle, + promptGroupId: promptGroupId.aiAssistant, + provider: 'bedrock', + prompt: { + default: BEDROCK_CHAT_TITLE, + }, + }, + { + promptId: promptDictionary.chatTitle, + promptGroupId: promptGroupId.aiAssistant, + provider: 'gemini', + prompt: { + default: GEMINI_CHAT_TITLE, + }, + }, ]; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/prompts.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/prompts.ts index fc3d7d68ecce0..3d931ab83eab1 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/prompts.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/prompts.ts @@ -23,7 +23,7 @@ export const GEMINI_SYSTEM_PROMPT = `${BASE_GEMINI_PROMPT} ${KB_CATCH} {include_ export const BEDROCK_SYSTEM_PROMPT = `Use tools as often as possible, as they have access to the latest data and syntax. Never return tags in the response, but make sure to include tags content in the response. Do not reflect on the quality of the returned search results in your response. ALWAYS return the exact response from NaturalLanguageESQLTool verbatim in the final response, without adding further description.`; export const GEMINI_USER_PROMPT = `Now, always using the tools at your disposal, step by step, come up with a response to this request:\n\n`; -export const STRUCTURED_SYSTEM_PROMPT = `Respond to the human as helpfully and accurately as possible. ${KNOWLEDGE_HISTORY} You have access to the following tools: +export const STRUCTURED_SYSTEM_PROMPT = `Respond to the human as helpfully and accurately as possible. ${KNOWLEDGE_HISTORY} {include_citations_prompt_placeholder} You have access to the following tools: {tools} @@ -131,3 +131,21 @@ export const ATTACK_DISCOVERY_GENERATION_SUMMARY_MARKDOWN = `A markdown summary export const ATTACK_DISCOVERY_GENERATION_TITLE = 'A short, no more than 7 words, title for the insight, NOT formatted with special syntax or markdown. This must be as brief as possible.'; export const ATTACK_DISCOVERY_GENERATION_INSIGHTS = `Insights with markdown that always uses special ${SYNTAX} syntax for field names and values from the source data. ${GOOD_SYNTAX_EXAMPLES} ${BAD_SYNTAX_EXAMPLES}`; + +export const BEDROCK_CHAT_TITLE = `You are a helpful assistant for Elastic Security. Assume the following user message is the start of a conversation between you and a user; give this conversation a title based on the content below. DO NOT UNDER ANY CIRCUMSTANCES wrap this title in single or double quotes. This title is shown in a list of conversations to the user, so title it for the user, not for you. Respond with the title only with no other text explaining your response. As an example, for the given MESSAGE, this is the TITLE: + +MESSAGE: I am having trouble with the Elastic Security app. +TITLE: Troubleshooting Elastic Security app issues +`; + +export const GEMINI_CHAT_TITLE = `You are a title generator for a helpful assistant for Elastic Security. Assume the following human message is the start of a conversation between you and a human. Generate a relevant conversation title for the human's message in plain text. Make sure the title is formatted for the user, without using quotes or markdown. The title should clearly reflect the content of the message and be appropriate for a list of conversations. Respond only with the title. As an example, for the given MESSAGE, this is the TITLE: + +MESSAGE: I am having trouble with the Elastic Security app. +TITLE: Troubleshooting Elastic Security app issues +`; + +export const DEFAULT_CHAT_TITLE = `You are a helpful assistant for Elastic Security. Assume the following user message is the start of a conversation between you and a user; give this conversation a title based on the content below. DO NOT UNDER ANY CIRCUMSTANCES wrap this title in single or double quotes. This title is shown in a list of conversations to the user, so title it for the user, not for you. As an example, for the given MESSAGE, this is the TITLE: + +MESSAGE: I am having trouble with the Elastic Security app. +TITLE: Troubleshooting Elastic Security app issues +`; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/tool_prompts.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/tool_prompts.ts new file mode 100644 index 0000000000000..37fdf89439be9 --- /dev/null +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/lib/prompt/tool_prompts.ts @@ -0,0 +1,82 @@ +/* + * Copyright 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 { Prompt } from '@kbn/security-ai-prompts'; + +export const promptGroupId = 'security-tools'; + +// promptId must match tool name +// cannot import from security_solution because it causes a circular dependency +export const localToolPrompts: Prompt[] = [ + { + promptId: 'AlertCountsTool', + promptGroupId, + prompt: { + default: + 'Call this for the counts of last 24 hours of open and acknowledged alerts in the environment, grouped by their severity and workflow status. The response will be JSON and from it you can summarize the information to answer the question.', + }, + }, + { + promptId: 'NaturalLanguageESQLTool', + promptGroupId, + prompt: { + default: `You MUST use the "NaturalLanguageESQLTool" function when the user wants to: + - breakdown or filter ES|QL queries that are displayed on the current page + - convert queries from another language to ES|QL + - asks general questions about ES|QL + + ALWAYS use this tool to generate ES|QL queries or explain anything about the ES|QL query language rather than coming up with your own answer.`, + }, + }, + { + promptId: 'ProductDocumentationTool', + promptGroupId, + prompt: { + default: + 'Use this tool to retrieve documentation about Elastic products. You can retrieve documentation about the Elastic stack, such as Kibana and Elasticsearch, or for Elastic solutions, such as Elastic Security, Elastic Observability or Elastic Enterprise Search.', + }, + }, + { + promptId: 'KnowledgeBaseRetrievalTool', + promptGroupId, + prompt: { + default: + "Call this for fetching details from the user's knowledge base. The knowledge base contains useful information the user wants to store between conversation contexts. Call this function when the user asks for information about themself, like 'what is my favorite...' or 'using my saved....'. Input must always be the free-text query on a single line, with no other text. You are welcome to re-write the query to be a summary of items/things to search for in the knowledge base, as a vector search will be performed to return similar results when requested. If the results returned do not look relevant, disregard and tell the user you were unable to find the information they were looking for. All requests include a `knowledge history` section which includes some existing knowledge of the user. DO NOT CALL THIS FUNCTION if the `knowledge history` sections appears to be able to answer the user's query.", + }, + }, + { + promptId: 'KnowledgeBaseWriteTool', + promptGroupId, + prompt: { + default: + "Call this for writing details to the user's knowledge base. The knowledge base contains useful information the user wants to store between conversation contexts. Input will be the summarized knowledge base entry to store, a short UI friendly name for the entry, and whether or not the entry is required.", + }, + }, + { + promptId: 'SecurityLabsKnowledgeBaseTool', + promptGroupId, + prompt: { + default: + 'Call this for knowledge from Elastic Security Labs content, which contains information on malware, attack techniques, and more.', + }, + }, + { + promptId: 'OpenAndAcknowledgedAlertsTool', + promptGroupId, + prompt: { + default: + 'Call this for knowledge about the latest n open and acknowledged alerts (sorted by `kibana.alert.risk_score`) in the environment, or when answering questions about open alerts. Do not call this tool for alert count or quantity. The output is an array of the latest n open and acknowledged alerts.', + }, + }, + { + promptId: 'defendInsightsTool', + promptGroupId, + prompt: { + default: 'Call this for Elastic Defend insights.', + }, + }, +]; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts index 2623f3ab60b75..1dc44448f3e38 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/chat/chat_complete_route.ts @@ -16,7 +16,7 @@ import { transformRawData, getAnonymizedValue, ConversationResponse, - contentReferencesStoreFactory, + newContentReferencesStore, pruneContentReferences, } from '@kbn/elastic-assistant-common'; import { buildRouteValidationWithZod } from '@kbn/elastic-assistant-common/impl/schemas/common'; @@ -186,8 +186,9 @@ export const chatCompleteRoute = ( })); } - const contentReferencesStore = - contentReferencesEnabled && contentReferencesStoreFactory(); + const contentReferencesStore = contentReferencesEnabled + ? newContentReferencesStore() + : undefined; const onLlmResponse = async ( content: string, diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts index db7c2d058069f..88ba28c06b8c7 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/helpers.ts @@ -124,7 +124,7 @@ export function getAssistantToolParams({ langSmithProject?: string; langSmithApiKey?: string; logger: Logger; - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; latestReplacements: Replacements; onNewReplacements: (newReplacements: Replacements) => void; request: KibanaRequest; @@ -136,7 +136,7 @@ export function getAssistantToolParams({ langChainTimeout: number; llm: ActionsClientLlm; logger: Logger; - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; replacements: Replacements; onNewReplacements: (newReplacements: Replacements) => void; request: KibanaRequest; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.test.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.test.ts index ade29634adf48..e71de9f426e47 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.test.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.test.ts @@ -10,6 +10,7 @@ import type { AuthenticatedUser } from '@kbn/core-security-common'; import type { DefendInsightsPostRequestBody } from '@kbn/elastic-assistant-common'; +import { getPrompt } from '@kbn/security-ai-prompts'; import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { actionsMock } from '@kbn/actions-plugin/server/mocks'; import { OpenAiProviderType } from '@kbn/stack-connectors-plugin/common/openai/constants'; @@ -29,8 +30,10 @@ import { getAssistantTool, createDefendInsight, isDefendInsightsEnabled } from ' import { postDefendInsightsRoute } from './post_defend_insights'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; +jest.mock('@kbn/security-ai-prompts'); jest.mock('./helpers'); +const getPromptMock = getPrompt as jest.Mock; describe('postDefendInsightsRoute', () => { let server: ReturnType; let context: ElasticAssistantRequestHandlerContextMock; @@ -80,6 +83,7 @@ describe('postDefendInsightsRoute', () => { langSmithApiKey: 'langSmithApiKey', }; } + const getTool = jest.fn(); beforeEach(() => { const tools = requestContextMock.createTools(); @@ -94,7 +98,8 @@ describe('postDefendInsightsRoute', () => { mockDataClient = getDefaultDataClient(); mockApiConfig = getDefaultApiConfig(); mockRequestBody = getDefaultRequestBody(); - (getAssistantTool as jest.Mock).mockReturnValue({ getTool: jest.fn() }); + getPromptMock.mockResolvedValue('prompt'); + (getAssistantTool as jest.Mock).mockReturnValue({ getTool, name: 'test-tool' }); (createDefendInsight as jest.Mock).mockResolvedValue({ currentInsight: mockCurrentInsight, defendInsightId: mockCurrentInsight.id, @@ -196,4 +201,23 @@ describe('postDefendInsightsRoute', () => { status_code: 500, }); }); + + it('should call getPrompt for tool description', async () => { + await server.inject( + postDefendInsightsRequest(mockRequestBody), + requestContextMock.convertContext(context) + ); + expect(getPromptMock).toHaveBeenCalledWith( + expect.objectContaining({ + connectorId: 'connector-id', + promptId: 'test-tool', + promptGroupId: 'security-tools', + }) + ); + expect(getTool).toHaveBeenCalledWith( + expect.objectContaining({ + description: 'prompt', + }) + ); + }); }); diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.ts index fb5adfa05bee0..c8ba9e9819b19 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/defend_insights/post_defend_insights.ts @@ -20,6 +20,8 @@ import { import { transformError } from '@kbn/securitysolution-es-utils'; import { IRouter, Logger } from '@kbn/core/server'; +import { getPrompt } from '@kbn/security-ai-prompts'; +import { localToolPrompts, promptGroupId } from '../../lib/prompt/tool_prompts'; import { buildResponse } from '../../lib/build_response'; import { ElasticAssistantRequestHandlerContext } from '../../types'; import { DEFAULT_PLUGIN_NAME, getPluginNameFromRequest } from '../helpers'; @@ -71,6 +73,7 @@ export const postDefendInsightsRoute = (router: IRouter tool.getTool(assistantToolParams) ?? [] - ); + const tools: StructuredTool[] = ( + await Promise.all( + assistantTools.map(async (tool) => { + let description: string | undefined; + try { + description = await getPrompt({ + actionsClient, + connector, + connectorId: connector.id, + model: getModelOrOss(llmType, isOssModel), + localPrompts: localToolPrompts, + promptId: tool.name, + promptGroupId: toolsGroupId, + provider: llmType, + savedObjectsClient, + }); + } catch (e) { + logger.error(`Failed to get prompt for tool: ${tool.name}`); + } + return tool.getTool({ + ...assistantToolParams, + llm: createLlmInstance(), + isOssModel, + description, + }); + }) + ) + ).filter((e) => e != null) as StructuredTool[]; - const defaultSystemPrompt = await getPrompt({ + const defaultSystemPrompt = await localGetPrompt({ actionsClient, connector, connectorId: connector.id, diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/helpers.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/helpers.ts index 9adc52ea620e1..d97a6b77f93b3 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/helpers.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/helpers.ts @@ -232,7 +232,7 @@ export interface LangChainExecuteParams { telemetry: AnalyticsServiceSetup; actionTypeId: string; connectorId: string; - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; llmTasks?: LlmTasksPluginStart; inference: InferenceServerStart; isOssModel?: boolean; diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts index e6df1f2ba3d08..685863cfa97b1 100644 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts @@ -12,7 +12,7 @@ import { getRequestAbortedSignal } from '@kbn/data-plugin/server'; import { schema } from '@kbn/config-schema'; import { API_VERSIONS, - contentReferencesStoreFactory, + newContentReferencesStore, ExecuteConnectorRequestBody, Message, Replacements, @@ -119,8 +119,9 @@ export const postActionsConnectorExecuteRoute = ( }); const promptsDataClient = await assistantContext.getAIAssistantPromptsDataClient(); - const contentReferencesStore = - contentReferencesEnabled && contentReferencesStoreFactory(); + const contentReferencesStore = contentReferencesEnabled + ? newContentReferencesStore() + : undefined; onLlmResponse = async ( content: string, diff --git a/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts b/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts index 981b00d68e643..a979e1b20aba9 100755 --- a/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts +++ b/x-pack/solutions/security/plugins/elastic_assistant/server/types.ts @@ -239,7 +239,8 @@ export interface AssistantToolParams { inference?: InferenceServerStart; isEnabledKnowledgeBase: boolean; connectorId?: string; - contentReferencesStore: ContentReferencesStore | false; + contentReferencesStore: ContentReferencesStore | undefined; + description?: string; esClient: ElasticsearchClient; kbDataClient?: AIAssistantKnowledgeBaseDataClient; langChainTimeout?: number; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen.ts index 2cefcab9756b7..a4c51a07359ed 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen.ts @@ -15,20 +15,14 @@ */ import { z } from '@kbn/zod'; +import { isNonEmptyString } from '@kbn/zod-helpers'; import { AlertIds } from '../../model/alert.gen'; -import { NonEmptyString } from '../../model/primitives.gen'; export type AlertAssignees = z.infer; export const AlertAssignees = z.object({ - /** - * A list of users ids to assign. - */ - add: z.array(NonEmptyString), - /** - * A list of users ids to unassign. - */ - remove: z.array(NonEmptyString), + add: z.array(z.string().min(1).superRefine(isNonEmptyString)), + remove: z.array(z.string().min(1).superRefine(isNonEmptyString)), }); export type SetAlertAssigneesRequestBody = z.infer; @@ -37,9 +31,6 @@ export const SetAlertAssigneesRequestBody = z.object({ * Details about the assignees to assign and unassign. */ assignees: AlertAssignees, - /** - * List of alerts ids to assign and unassign passed assignees. - */ ids: AlertIds, }); export type SetAlertAssigneesRequestBodyInput = z.input; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.schema.yaml index b4b5e858672dd..6c28c76a0b29a 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_assignees/set_alert_assignees_route.schema.yaml @@ -28,10 +28,42 @@ paths: description: Details about the assignees to assign and unassign. ids: $ref: '../../model/alert.schema.yaml#/components/schemas/AlertIds' - description: List of alerts ids to assign and unassign passed assignees. + examples: + add: + value: + assignees: + add: ['u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0'] + remove: [] + ids: ['681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6'] + remove: + value: + assignees: + add: [] + remove: ['u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0'] + ids: ['681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6'] responses: 200: description: Indicates a successful call. + content: + application/ndjson: + examples: + add: + value: + took: 76, + timed_out: false, + total: 1, + updated: 1, + deleted: 0, + batches: 1, + version_conflicts: 0, + noops: 0, + retries: + - bulk: 0, + - search: 0 + throttled_millis: 0, + requests_per_second: -1, + throttled_until_millis: 0, + failures: [] 400: description: Invalid request. @@ -46,10 +78,14 @@ components: add: type: array items: - $ref: '../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' - description: A list of users ids to assign. + type: string + format: nonempty + minLength: 1 + description: A list of users ids to assign. remove: type: array items: - $ref: '../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' - description: A list of users ids to unassign. + type: string + format: nonempty + minLength: 1 + description: A list of users ids to unassign. diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts index b08d4ff877058..f4b40c03a634a 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts @@ -18,6 +18,9 @@ import { z } from '@kbn/zod'; import { AlertIds, AlertTags } from '../../../model/alert.gen'; +/** + * Object with list of tags to add and remove. + */ export type SetAlertTags = z.infer; export const SetAlertTags = z.object({ tags_to_add: AlertTags, diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml index 2e712ed3fec40..e80be52c99415 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml @@ -30,6 +30,19 @@ paths: required: - ids - tags + examples: + add: + value: + tags: + tags_to_add: ['Duplicate'] + tags_to_remove: [] + ids: ['549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e'] + remove: + value: + tags: + tags_to_add: [] + tags_to_remove: ['Duplicate'] + ids: ['549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e'] responses: 200: description: Successful response @@ -39,6 +52,24 @@ paths: type: object additionalProperties: true description: Elasticsearch update by query response + examples: + success: + value: + took: 68, + timed_out: false, + total: 1, + updated: 1, + deleted: 0, + batches: 1, + version_conflicts: 0, + noops: 0, + retries: + bulk: 0, + search: 0 + throttled_millis: 0, + requests_per_second: -1, + throttled_until_millis: 0, + failures: [] 400: description: Invalid input data response content: @@ -63,6 +94,7 @@ paths: components: schemas: SetAlertTags: + description: Object with list of tags to add and remove. type: object properties: tags_to_add: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml index 70283f59ef79d..f9a854689c490 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_index/read_index.schema.yaml @@ -25,6 +25,11 @@ paths: type: boolean nullable: true required: [name, index_mapping_outdated] + examples: + success: + value: + index_mapping_outdated: false + name: '.alerts-security.alerts-default' 401: description: Unsuccessful authentication response content: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_privileges/read_privileges.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_privileges/read_privileges.schema.yaml index 168ad44849014..02239060325dc 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_privileges/read_privileges.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/index_management/read_privileges/read_privileges.schema.yaml @@ -29,6 +29,42 @@ paths: has_encryption_key: type: boolean required: [is_authenticated, has_encryption_key] + examples: + success: + value: + username: elastic + has_all_requested: true + cluster: + all: true + monitor_ml: true + manage_transform: true + manage_index_templates: true + monitor_transform: true + manage_ml: true + monitor: true + manage_pipeline: true + manage_api_key: true + manage_security: true + manage_own_api_key: true + manage: true + index: + .alerts-security.alerts-default: + all: true + create: true + create_doc: true + create_index: true + delete: true + delete_index: true + index: true + maintenance: true + manage: true + monitor: true + read: true + view_index_metadata: true + write: true + application: {} + is_authenticated: true + has_encryption_key: true 401: description: Unsuccessful authentication response content: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml index 00061cf50c60d..b16899d755599 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml @@ -48,6 +48,35 @@ paths: sort: $ref: '#/components/schemas/AlertsSort' description: Elasticsearch query and aggregation request + examples: + query: + value: + size: 0 + query: + bool: + filter: + - bool: + must: [] + filter: + - match_phrase: + kibana.alert.workflow_status: open + should: [] + must_not: + - exists: + field: kibana.alert.building_block_type + - range: + '@timestamp': + gte: 2025-01-17T08:00:00.000Z + lte: 2025-01-18T07:59:59.999Z + aggs: + alertsByGrouping: + terms: + field: host.name + size: 10 + missingFields: + missing: + field: host.name + runtime_mappings: {} responses: 200: description: Successful response @@ -57,6 +86,31 @@ paths: type: object additionalProperties: true description: Elasticsearch search response + examples: + success: + value: + took: 0 + timed_out: false + _shards: + total: 1 + successful: 1 + skipped: 0 + failed: 0 + hits: + total: + value: 5 + relation: eq + max_score: null + hits: [] + aggregations: + alertsByGrouping: + doc_count_error_upper_bound: 0 + sum_other_doc_count: 0 + buckets: + - key: Host-f43kkddfyc + doc_count: 5 + missingFields: + doc_count: 0 400: description: Invalid input data response content: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts index c8def8a2fb305..b95be98a5c897 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts @@ -15,13 +15,16 @@ */ import { z } from '@kbn/zod'; +import { isNonEmptyString } from '@kbn/zod-helpers'; -import { NonEmptyString } from '../../../model/primitives.gen'; import { AlertStatus } from '../../../model/alert.gen'; export type SetAlertsStatusByIds = z.infer; export const SetAlertsStatusByIds = z.object({ - signal_ids: z.array(NonEmptyString).min(1), + /** + * List of alert `id`s. + */ + signal_ids: z.array(z.string().min(1).superRefine(isNonEmptyString)).min(1), status: AlertStatus, }); diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml index fe514c4dafe2e..2f3266254ebd3 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml @@ -21,6 +21,42 @@ paths: oneOf: - $ref: '#/components/schemas/SetAlertsStatusByIds' - $ref: '#/components/schemas/SetAlertsStatusByQuery' + examples: + byId: + value: + status: closed + signal_ids: ['80e1383f856e67c1b7f7a1634744fa6d66b6e2ef7aa26d226e57afb5a7b2b4a1'] + byQuery: + value: + conflicts: proceed + status: closed + query: + bool: + must: [] + filter: + - range: + '@timestamp': + gte: 2024-10-23T07:00:00.000Z + lte: 2025-01-21T20:12:11.704Z + format: strict_date_optional_time + - bool: + filter: + bool: + must: [] + filter: + - match_phrase: + kibana.alert.workflow_status: open + - range: + '@timestamp': + gte: 2024-10-23T07:00:00.000Z + lte: 2025-01-21T20:12:11.704Z + format: strict_date_optional_time + should: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + must_not: [] responses: 200: description: Successful response @@ -30,6 +66,41 @@ paths: type: object additionalProperties: true description: Elasticsearch update by query response + examples: + byId: + value: + took: 81 + timed_out: false + total: 1 + updated: 1 + deleted: 0 + batches: 1 + version_conflicts: 0 + noops: 0 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + requests_per_second: -1 + throttled_until_millis: 0 + failures: [] + byQuery: + value: + took: 100 + timed_out: false + total: 17 + updated: 17 + deleted: 0 + batches: 1 + version_conflicts: 0 + noops: 0 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + requests_per_second: -1 + throttled_until_millis: 0 + failures: [] 400: description: Invalid input data response content: @@ -58,8 +129,11 @@ components: properties: signal_ids: type: array + description: List of alert `id`s. items: - $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + type: string + format: nonempty + minLength: 1 minItems: 1 status: $ref: '../../../model/alert.schema.yaml#/components/schemas/AlertStatus' diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts index 7f321de7c06da..8dd0f18334724 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts @@ -15,13 +15,21 @@ */ import { z } from '@kbn/zod'; - -import { NonEmptyString } from '../../../model/primitives.gen'; +import { isNonEmptyString } from '@kbn/zod-helpers'; export type AlertsReindexOptions = z.infer; export const AlertsReindexOptions = z.object({ + /** + * The throttle for the migration task in sub-requests per second. Corresponds to requests_per_second on the Reindex API. + */ requests_per_second: z.number().int().min(1).optional(), + /** + * Number of alerts to migrate per batch. Corresponds to the source.size option on the Reindex API. + */ size: z.number().int().min(1).optional(), + /** + * The number of subtasks for the migration task. Corresponds to slices on the Reindex API. + */ slices: z.number().int().min(1).optional(), }); @@ -49,7 +57,10 @@ export const SkippedAlertsIndexMigration = z.object({ export type CreateAlertsMigrationRequestBody = z.infer; export const CreateAlertsMigrationRequestBody = z .object({ - index: z.array(NonEmptyString).min(1), + /** + * Array of index names to migrate. + */ + index: z.array(z.string().min(1).superRefine(isNonEmptyString)).min(1), }) .merge(AlertsReindexOptions); export type CreateAlertsMigrationRequestBodyInput = z.input< diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml index d77bb820ec59e..0196e031f8724 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml @@ -25,13 +25,19 @@ paths: - type: object properties: index: + description: Array of index names to migrate. type: array items: - $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + type: string + format: nonempty + minLength: 1 minItems: 1 required: [index] - $ref: '#/components/schemas/AlertsReindexOptions' - + examples: + singleIndex: + value: + index: [.siem-signals-default-000001] responses: 200: description: Successful response @@ -48,6 +54,13 @@ paths: - $ref: '#/components/schemas/AlertsIndexMigrationError' - $ref: '#/components/schemas/SkippedAlertsIndexMigration' required: [indices] + examples: + success: + value: + indices: + - index: .siem-signals-default-000001, + migration_id: 923f7c50-505f-11eb-ae0a-3fa2e626a51d + migration_index: .siem-signals-default-000001-r000016 400: description: Invalid input data response content: @@ -77,12 +90,15 @@ components: requests_per_second: type: integer minimum: 1 + description: The throttle for the migration task in sub-requests per second. Corresponds to requests_per_second on the Reindex API. size: type: integer minimum: 1 + description: Number of alerts to migrate per batch. Corresponds to the source.size option on the Reindex API. slices: type: integer minimum: 1 + description: The number of subtasks for the migration task. Corresponds to slices on the Reindex API. AlertsIndexMigrationSuccess: type: object diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts index b9d9604f5449b..47b043706f40c 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts @@ -34,6 +34,9 @@ export const MigrationCleanupResult = z.object({ export type AlertsMigrationCleanupRequestBody = z.infer; export const AlertsMigrationCleanupRequestBody = z.object({ + /** + * Array of `migration_id`s to cleanup. + */ migration_ids: z.array(z.string()).min(1), }); export type AlertsMigrationCleanupRequestBodyInput = z.input< diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml index a03d7c476d65f..458f594091ddd 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml @@ -14,7 +14,7 @@ paths: Migrations favor data integrity over shard size. Consequently, unused or orphaned indices are artifacts of the migration process. A successful migration will result in both the old and new indices being present. As such, the old, orphaned index can (and likely should) be deleted. - + While you can delete these indices manually, the endpoint accomplishes this task by applying a deletion policy to the relevant index, causing it to be deleted after 30 days. It also deletes other artifacts specific to the migration implementation. @@ -29,11 +29,14 @@ paths: type: object properties: migration_ids: + description: Array of `migration_id`s to cleanup. type: array items: type: string minItems: 1 required: [migration_ids] + example: + migration_ids: [924f7c50-505f-11eb-ae0a-3fa2e626a51d] responses: 200: description: Successful response @@ -43,6 +46,16 @@ paths: type: array items: $ref: '#/components/schemas/MigrationCleanupResult' + examples: + success: + value: + migrations: + - id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + destinationIndex: .siem-signals-default-000002-r000016 + status: success + sourceIndex: .siem-signals-default-000002 + version: 16 + updated: 2021-01-06T22:05:56.859Z 400: description: Invalid input data response content: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts index d337beffb9f45..d5da5fcc892d8 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts @@ -35,6 +35,9 @@ export const MigrationFinalizationResult = z.object({ export type FinalizeAlertsMigrationRequestBody = z.infer; export const FinalizeAlertsMigrationRequestBody = z.object({ + /** + * Array of `migration_id`s to finalize. + */ migration_ids: z.array(z.string()).min(1), }); export type FinalizeAlertsMigrationRequestBodyInput = z.input< diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml index 1160467494090..03ec7e4813227 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml @@ -25,11 +25,14 @@ paths: type: object properties: migration_ids: + description: Array of `migration_id`s to finalize. type: array items: type: string minItems: 1 required: [migration_ids] + example: + migration_ids: ['924f7c50-505f-11eb-ae0a-3fa2e626a51d'] responses: 200: description: Successful response @@ -39,6 +42,17 @@ paths: type: array items: $ref: '#/components/schemas/MigrationFinalizationResult' + examples: + success: + value: + migrations: + - id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + completed: true + destinationIndex: '.siem-signals-default-000002-r000016' + status: success + sourceIndex: '.siem-signals-default-000002' + version: 16 + updated: '2021-01-06T22:05:56.859Z' 400: description: Invalid input data response content: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/read_signals_migration_status/read_signals_migration_status.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/read_signals_migration_status/read_signals_migration_status.schema.yaml index 0baa8e2281506..27688f9867f4b 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/read_signals_migration_status/read_signals_migration_status.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/detection_engine/signals_migration/read_signals_migration_status/read_signals_migration_status.schema.yaml @@ -4,7 +4,7 @@ info: version: '2023-10-31' paths: /api/detection_engine/signals/migration_status: - post: + get: x-labels: [ess] operationId: ReadAlertsMigrationStatus x-codegen-enabled: true @@ -24,6 +24,7 @@ paths: Time from which data is analyzed. For example, now-4200s means the rule analyzes data from 70 minutes before its start time. Defaults to now-6m (analyzes data from 6 minutes before the start time). format: date-math + example: now-30d responses: 200: description: Successful response @@ -37,6 +38,30 @@ paths: items: $ref: '#/components/schemas/IndexMigrationStatus' required: [indices] + examples: + success: + value: + indices: + - index: .siem-signals-default-000002 + version: 15 + signal_versions: + - version: 15 + count: 100 + - version: 16 + count: 87 + migrations: + - id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + status: pending + version: 16 + updated: 2021-01-06T20:41:37.173Z + is_outdated: true + - index: .siem-signals-default-000003 + version: 16 + signal_versions: + - version: 16 + count: 54 + migrations: [] + is_outdated: false 400: description: Invalid input data response content: diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts index b5684adfe6ff6..030ba1433fb7b 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.gen.ts @@ -16,10 +16,10 @@ import type { z } from '@kbn/zod'; -import { SuccessResponse, NoParametersRequestSchema } from '../../../model/schema/common.gen'; +import { SuccessResponse, BaseActionSchema } from '../../../model/schema/common.gen'; export type IsolateRouteRequestBody = z.infer; -export const IsolateRouteRequestBody = NoParametersRequestSchema; +export const IsolateRouteRequestBody = BaseActionSchema; export type EndpointIsolateActionRequestBody = z.infer; export const EndpointIsolateActionRequestBody = IsolateRouteRequestBody; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml index 5bfd57a4e406d..396d8e3d54b1e 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/isolate/isolate.schema.yaml @@ -27,4 +27,4 @@ paths: components: schemas: IsolateRouteRequestBody: - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/NoParametersRequestSchema' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts index da7338bc62fe9..63e31a863e58e 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.gen.ts @@ -16,10 +16,10 @@ import type { z } from '@kbn/zod'; -import { SuccessResponse, NoParametersRequestSchema } from '../../../model/schema/common.gen'; +import { SuccessResponse, BaseActionSchema } from '../../../model/schema/common.gen'; export type GetProcessesRouteRequestBody = z.infer; -export const GetProcessesRouteRequestBody = NoParametersRequestSchema; +export const GetProcessesRouteRequestBody = BaseActionSchema; export type EndpointGetProcessesActionRequestBody = z.infer< typeof EndpointGetProcessesActionRequestBody diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml index d49af9a84bc8e..dc2735e04b50f 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/running_procs/running_procs.schema.yaml @@ -28,4 +28,4 @@ components: schemas: GetProcessesRouteRequestBody: allOf: - - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/NoParametersRequestSchema' + - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts index 924e139f4731d..115ff4162e206 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.gen.ts @@ -16,10 +16,10 @@ import type { z } from '@kbn/zod'; -import { SuccessResponse, NoParametersRequestSchema } from '../../../model/schema/common.gen'; +import { SuccessResponse, BaseActionSchema } from '../../../model/schema/common.gen'; export type UnisolateRouteRequestBody = z.infer; -export const UnisolateRouteRequestBody = NoParametersRequestSchema; +export const UnisolateRouteRequestBody = BaseActionSchema; export type EndpointUnisolateActionRequestBody = z.infer; export const EndpointUnisolateActionRequestBody = UnisolateRouteRequestBody; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml index 344f1c89eb096..6f5d2087c556e 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/endpoint/actions/response_actions/unisolate/unisolate.schema.yaml @@ -27,4 +27,4 @@ paths: components: schemas: UnisolateRouteRequestBody: - $ref: '../../../model/schema/common.schema.yaml#/components/schemas/NoParametersRequestSchema' + $ref: '../../../model/schema/common.schema.yaml#/components/schemas/BaseActionSchema' diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.gen.ts index 04a32d7866d0a..bb9cf4354684d 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.gen.ts @@ -15,21 +15,29 @@ */ import { z } from '@kbn/zod'; - -import { NonEmptyString } from './primitives.gen'; +import { isNonEmptyString } from '@kbn/zod-helpers'; /** - * A list of alerts ids. + * A list of alerts `id`s. */ export type AlertIds = z.infer; -export const AlertIds = z.array(NonEmptyString).min(1); +export const AlertIds = z.array(z.string().min(1).superRefine(isNonEmptyString)).min(1); +/** + * Use alert tags to organize related alerts into categories that you can filter and group. + */ export type AlertTag = z.infer; -export const AlertTag = NonEmptyString; +export const AlertTag = z.string().min(1).superRefine(isNonEmptyString); +/** + * List of keywords to organize related alerts into categories that you can filter and group. + */ export type AlertTags = z.infer; export const AlertTags = z.array(AlertTag); +/** + * The status of an alert, which can be `open`, `acknowledged`, `in-progress`, or `closed`. + */ export type AlertStatus = z.infer; export const AlertStatus = z.enum(['open', 'closed', 'acknowledged', 'in-progress']); export type AlertStatusEnum = typeof AlertStatus.enum; diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.schema.yaml b/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.schema.yaml index ecf7e02d6ebe3..9304c1acdffc5 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/common/api/model/alert.schema.yaml @@ -9,19 +9,26 @@ components: AlertIds: type: array items: - $ref: './primitives.schema.yaml#/components/schemas/NonEmptyString' + type: string + minLength: 1 + format: nonempty minItems: 1 - description: A list of alerts ids. + description: A list of alerts `id`s. AlertTag: - $ref: './primitives.schema.yaml#/components/schemas/NonEmptyString' + type: string + format: nonempty + minLength: 1 + description: Use alert tags to organize related alerts into categories that you can filter and group. AlertTags: type: array + description: List of keywords to organize related alerts into categories that you can filter and group. items: $ref: '#/components/schemas/AlertTag' AlertStatus: + description: The status of an alert, which can be `open`, `acknowledged`, `in-progress`, or `closed`. type: string enum: - open diff --git a/x-pack/solutions/security/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/solutions/security/plugins/security_solution/common/api/quickstart_client.gen.ts index a57be4b8f0680..1ed3a770d3410 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -1912,7 +1912,7 @@ finalize it. headers: { [ELASTIC_HTTP_VERSION_HEADER]: '2023-10-31', }, - method: 'POST', + method: 'GET', query: props.query, }) diff --git a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.test.ts b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.test.ts index 03dc7ecbe5331..7ba6eaceb35b2 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { Filter } from '@kbn/es-query'; import { KqlQueryType } from '../../../api/detection_engine'; import { extractRuleEqlQuery, @@ -12,6 +13,24 @@ import { extractRuleKqlQuery, } from './extract_rule_data_query'; +const mockFilter: Filter = { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'test', + params: { + query: 'value', + }, + }, + query: { + term: { + field: 'value', + }, + }, +}; + describe('extract rule data queries', () => { describe('extractRuleKqlQuery', () => { it('extracts a trimmed version of the query field for inline query types', () => { @@ -24,6 +43,39 @@ describe('extract rule data queries', () => { filters: [], }); }); + + it('normalizes filters', () => { + const extractedKqlQuery = extractRuleKqlQuery( + 'event.kind:alert', + 'kuery', + [mockFilter], + undefined + ); + + expect(extractedKqlQuery).toEqual({ + type: KqlQueryType.inline_query, + query: 'event.kind:alert', + language: 'kuery', + filters: [ + { + meta: { + negate: false, + disabled: false, + type: 'phrase', + key: 'test', + params: { + query: 'value', + }, + }, + query: { + term: { + field: 'value', + }, + }, + }, + ], + }); + }); }); describe('extractRuleEqlQuery', () => { diff --git a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.ts b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.ts index 04460dd0cad75..e6de1e12d72dc 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_rule_data_query.ts @@ -5,6 +5,7 @@ * 2.0. */ +import type { Filter } from '@kbn/es-query'; import type { EqlQueryLanguage, EsqlQueryLanguage, @@ -48,7 +49,7 @@ export const extractInlineKqlQuery = ( type: KqlQueryType.inline_query, query: query?.trim() ?? '', language: language ?? 'kuery', - filters: filters ?? [], + filters: normalizeFilterArray(filters), }; }; @@ -65,7 +66,7 @@ export const extractRuleEqlQuery = (params: ExtractRuleEqlQueryParams): RuleEqlQ return { query: params.query.trim(), language: params.language, - filters: params.filters ?? [], + filters: normalizeFilterArray(params.filters), event_category_override: params.eventCategoryOverride, timestamp_field: params.timestampField, tiebreaker_field: params.tiebreakerField, @@ -81,3 +82,22 @@ export const extractRuleEsqlQuery = ( language, }; }; + +/** + * Removes the null `alias` field that gets appended from the internal kibana filter util for comparison + * Relevant issue: https://github.com/elastic/kibana/issues/202966 + */ +const normalizeFilterArray = (filters: RuleFilterArray | undefined): RuleFilterArray => { + if (!filters?.length) { + return []; + } + return (filters as Filter[]).map((filter) => ({ + ...filter, + meta: filter.meta + ? { + ...filter.meta, + alias: filter.meta.alias ?? undefined, + } + : undefined, + })); +}; diff --git a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.test.ts b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.test.ts index 115ea26c9ea83..8e226b8492729 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.test.ts @@ -12,6 +12,87 @@ import { extractThreatArray } from './extract_threat_array'; const mockThreat = getThreatMock()[0]; describe('extractThreatArray', () => { + it('normalizes url ending backslashes', () => { + const mockRule = { + ...getRulesSchemaMock(), + threat: [ + { + ...mockThreat, + tactic: { + ...mockThreat.tactic, + reference: 'https://attack.mitre.org/tactics/TA0000', + }, + technique: [ + { + ...mockThreat.technique![0], + reference: 'https://attack.mitre.org/techniques/T0000/', + subtechnique: [ + { + ...mockThreat.technique![0].subtechnique![0], + reference: 'https://attack.mitre.org/techniques/T0000/000', + }, + ], + }, + ], + }, + ], + }; + const normalizedThreatArray = extractThreatArray(mockRule); + + expect(normalizedThreatArray).toEqual([ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0000', + name: 'test tactic', + reference: 'https://attack.mitre.org/tactics/TA0000/', + }, + technique: [ + { + id: 'T0000', + name: 'test technique', + reference: 'https://attack.mitre.org/techniques/T0000/', + subtechnique: [ + { + id: 'T0000.000', + name: 'test subtechnique', + reference: 'https://attack.mitre.org/techniques/T0000/000/', + }, + ], + }, + ], + }, + ]); + }); + + it('normalizes url ending backslashes with query strings', () => { + const mockRule = { + ...getRulesSchemaMock(), + threat: [ + { + ...mockThreat, + tactic: { + ...mockThreat.tactic, + reference: 'https://attack.mitre.org/tactics/TA0000?query=test', + }, + technique: [], + }, + ], + }; + const normalizedThreatArray = extractThreatArray(mockRule); + + expect(normalizedThreatArray).toEqual([ + { + framework: 'MITRE ATT&CK', + tactic: { + id: 'TA0000', + name: 'test tactic', + reference: 'https://attack.mitre.org/tactics/TA0000/?query=test', + }, + }, + ]); + }); + it('trims empty technique fields from threat object', () => { const mockRule = { ...getRulesSchemaMock(), threat: [{ ...mockThreat, technique: [] }] }; const normalizedThreatArray = extractThreatArray(mockRule); diff --git a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.ts b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.ts index 019d8ceee4f5e..2208454d69cdb 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/detection_engine/prebuilt_rules/diff/extract_threat_array.ts @@ -8,21 +8,56 @@ import type { RuleResponse, ThreatArray, + ThreatSubtechnique, ThreatTechnique, } from '../../../api/detection_engine/model/rule_schema'; export const extractThreatArray = (rule: RuleResponse): ThreatArray => rule.threat.map((threat) => { if (threat.technique && threat.technique.length) { - return { ...threat, technique: trimTechniqueArray(threat.technique) }; + return { + ...threat, + tactic: { ...threat.tactic, reference: normalizeThreatReference(threat.tactic.reference) }, + technique: trimTechniqueArray(threat.technique), + }; } - return { ...threat, technique: undefined }; // If `technique` is an empty array, remove the field from the `threat` object + return { + ...threat, + tactic: { ...threat.tactic, reference: normalizeThreatReference(threat.tactic.reference) }, + technique: undefined, + }; // If `technique` is an empty array, remove the field from the `threat` object }); const trimTechniqueArray = (techniqueArray: ThreatTechnique[]): ThreatTechnique[] => { return techniqueArray.map((technique) => ({ ...technique, + reference: normalizeThreatReference(technique.reference), subtechnique: - technique.subtechnique && technique.subtechnique.length ? technique.subtechnique : undefined, // If `subtechnique` is an empty array, remove the field from the `technique` object + technique.subtechnique && technique.subtechnique.length + ? trimSubtechniqueArray(technique.subtechnique) + : undefined, // If `subtechnique` is an empty array, remove the field from the `technique` object })); }; + +const trimSubtechniqueArray = (subtechniqueArray: ThreatSubtechnique[]): ThreatSubtechnique[] => { + return subtechniqueArray.map((subtechnique) => ({ + ...subtechnique, + reference: normalizeThreatReference(subtechnique.reference), + })); +}; + +const normalizeThreatReference = (reference: string): string => { + try { + const parsed = new URL(reference); + + if (!parsed.pathname.endsWith('/')) { + // Adds a trailing backslash in urls if it doesn't exist to account for + // any inconsistencies between our script generated data and prebuilt rules packages + parsed.pathname = `${parsed.pathname}/`; + } + + return parsed.toString(); + } catch { + return reference; + } +}; diff --git a/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml index 1aa026db6eac9..aa06e6b17ca00 100644 --- a/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_detections_api_2023_10_31.bundled.schema.yaml @@ -65,6 +65,11 @@ paths: '200': content: application/json: + examples: + success: + value: + index_mapping_outdated: false + name: .alerts-security.alerts-default schema: type: object properties: @@ -163,6 +168,42 @@ paths: '200': content: application/json: + examples: + success: + value: + application: {} + cluster: + all: true + manage: true + manage_api_key: true + manage_index_templates: true + manage_ml: true + manage_own_api_key: true + manage_pipeline: true + manage_security: true + manage_transform: true + monitor: true + monitor_ml: true + monitor_transform: true + has_all_requested: true + has_encryption_key: true + index: + .alerts-security.alerts-default: + all: true + create: true + create_doc: true + create_index: true + delete: true + delete_index: true + index: true + maintenance: true + manage: true + monitor: true + read: true + view_index_metadata: true + write: true + is_authenticated: true + username: elastic schema: type: object properties: @@ -1001,6 +1042,25 @@ paths: requestBody: content: application/json: + examples: + add: + value: + assignees: + add: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + remove: [] + ids: + - >- + 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 + remove: + value: + assignees: + add: [] + remove: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + ids: + - >- + 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 schema: type: object properties: @@ -1009,13 +1069,32 @@ paths: description: Details about the assignees to assign and unassign. ids: $ref: '#/components/schemas/AlertIds' - description: List of alerts ids to assign and unassign passed assignees. required: - assignees - ids required: true responses: '200': + content: + application/ndjson: + examples: + add: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + - bulk: 0, + - search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 76, + total: 1, + updated: 1, + version_conflicts: 0, description: Indicates a successful call. '400': description: Invalid request. @@ -1038,9 +1117,13 @@ paths: content: application/json: schema: + example: + migration_ids: + - 924f7c50-505f-11eb-ae0a-3fa2e626a51d type: object properties: migration_ids: + description: Array of `migration_id`s to finalize. items: type: string minItems: 1 @@ -1053,6 +1136,17 @@ paths: '200': content: application/json: + examples: + success: + value: + migrations: + - completed: true + destinationIndex: .siem-signals-default-000002-r000016 + id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + sourceIndex: .siem-signals-default-000002 + status: success + updated: '2021-01-06T22:05:56.859Z' + version: 16 schema: items: $ref: '#/components/schemas/MigrationFinalizationResult' @@ -1107,9 +1201,13 @@ paths: content: application/json: schema: + example: + migration_ids: + - 924f7c50-505f-11eb-ae0a-3fa2e626a51d type: object properties: migration_ids: + description: Array of `migration_id`s to cleanup. items: type: string minItems: 1 @@ -1122,6 +1220,16 @@ paths: '200': content: application/json: + examples: + success: + value: + migrations: + - destinationIndex: .siem-signals-default-000002-r000016 + id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + sourceIndex: .siem-signals-default-000002 + status: success + updated: 2021-01-06T22:05:56.859Z + version: 16 schema: items: $ref: '#/components/schemas/MigrationCleanupResult' @@ -1164,13 +1272,21 @@ paths: requestBody: content: application/json: + examples: + singleIndex: + value: + index: + - .siem-signals-default-000001 schema: allOf: - type: object properties: index: + description: Array of index names to migrate. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array required: @@ -1182,6 +1298,13 @@ paths: '200': content: application/json: + examples: + success: + value: + indices: + - index: .siem-signals-default-000001, + migration_id: 923f7c50-505f-11eb-ae0a-3fa2e626a51d + migration_index: .siem-signals-default-000001-r000016 schema: type: object properties: @@ -1220,7 +1343,7 @@ paths: - Security Detections API - Alerts migration API /api/detection_engine/signals/migration_status: - post: + get: deprecated: true description: >- Retrieve indices that contain detection alerts of a particular age, @@ -1238,12 +1361,37 @@ paths: before its start time. Defaults to now-6m (analyzes data from 6 minutes before the start time). + example: now-30d format: date-math type: string responses: '200': content: application/json: + examples: + success: + value: + indices: + - index: .siem-signals-default-000002 + is_outdated: true + migrations: + - id: 924f7c50-505f-11eb-ae0a-3fa2e626a51d + status: pending + updated: 2021-01-06T20:41:37.173Z + version: 16 + signal_versions: + - count: 100 + version: 15 + - count: 87 + version: 16 + version: 15 + - index: .siem-signals-default-000003 + is_outdated: false + migrations: [] + signal_versions: + - count: 54 + version: 16 + version: 16 schema: type: object properties: @@ -1285,6 +1433,35 @@ paths: requestBody: content: application/json: + examples: + query: + value: + aggs: + alertsByGrouping: + terms: + field: host.name + size: 10 + missingFields: + missing: + field: host.name + query: + bool: + filter: + - bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + - range: + '@timestamp': + gte: 2025-01-17T08:00:00.000Z + lte: 2025-01-18T07:59:59.999Z + runtime_mappings: {} + size: 0 schema: description: Elasticsearch query and aggregation request type: object @@ -1322,6 +1499,31 @@ paths: '200': content: application/json: + examples: + success: + value: + _shards: + failed: 0 + skipped: 0 + successful: 1 + total: 1 + aggregations: + alertsByGrouping: + buckets: + - doc_count: 5 + key: Host-f43kkddfyc + doc_count_error_upper_bound: 0 + sum_other_doc_count: 0 + missingFields: + doc_count: 0 + hits: + hits: [] + max_score: null + total: + relation: eq + value: 5 + timed_out: false + took: 0 schema: additionalProperties: true description: Elasticsearch search response @@ -1358,6 +1560,44 @@ paths: requestBody: content: application/json: + examples: + byId: + value: + signal_ids: + - >- + 80e1383f856e67c1b7f7a1634744fa6d66b6e2ef7aa26d226e57afb5a7b2b4a1 + status: closed + byQuery: + value: + conflicts: proceed + query: + bool: + filter: + - '@timestamp': + format: strict_date_optional_time + gte: 2024-10-23T07:00:00.000Z + lte: 2025-01-21T20:12:11.704Z + range: null + - bool: + filter: + bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + - '@timestamp': + format: strict_date_optional_time + gte: 2024-10-23T07:00:00.000Z + lte: 2025-01-21T20:12:11.704Z + range: null + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + must: [] + must_not: [] + should: [] + status: closed schema: oneOf: - $ref: '#/components/schemas/SetAlertsStatusByIds' @@ -1370,6 +1610,41 @@ paths: '200': content: application/json: + examples: + byId: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 81 + total: 1 + updated: 1 + version_conflicts: 0 + byQuery: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 100 + total: 17 + updated: 17 + version_conflicts: 0 schema: additionalProperties: true description: Elasticsearch update by query response @@ -1409,6 +1684,25 @@ paths: requestBody: content: application/json: + examples: + add: + value: + ids: + - >- + 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: + - Duplicate + tags_to_remove: [] + remove: + value: + ids: + - >- + 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: [] + tags_to_remove: + - Duplicate schema: type: object properties: @@ -1427,6 +1721,24 @@ paths: '200': content: application/json: + examples: + success: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + bulk: 0, + search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 68, + total: 1, + updated: 1, + version_conflicts: 0, schema: additionalProperties: true description: Elasticsearch update by query response @@ -1477,22 +1789,28 @@ components: type: object properties: add: - description: A list of users ids to assign. items: - $ref: '#/components/schemas/NonEmptyString' + description: A list of users ids to assign. + format: nonempty + minLength: 1 + type: string type: array remove: - description: A list of users ids to unassign. items: - $ref: '#/components/schemas/NonEmptyString' + description: A list of users ids to unassign. + format: nonempty + minLength: 1 + type: string type: array required: - add - remove AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array AlertsIndex: @@ -1537,12 +1855,21 @@ components: type: object properties: requests_per_second: + description: >- + The throttle for the migration task in sub-requests per second. + Corresponds to requests_per_second on the Reindex API. minimum: 1 type: integer size: + description: >- + Number of alerts to migrate per batch. Corresponds to the + source.size option on the Reindex API. minimum: 1 type: integer slices: + description: >- + The number of subtasks for the migration task. Corresponds to slices + on the Reindex API. minimum: 1 type: integer AlertsSort: @@ -1557,6 +1884,9 @@ components: - additionalProperties: true type: object AlertStatus: + description: >- + The status of an alert, which can be `open`, `acknowledged`, + `in-progress`, or `closed`. enum: - open - closed @@ -1610,8 +1940,16 @@ components: - suppress type: string AlertTag: - $ref: '#/components/schemas/NonEmptyString' + description: >- + Use alert tags to organize related alerts into categories that you can + filter and group. + format: nonempty + minLength: 1 + type: string AlertTags: + description: >- + List of keywords to organize related alerts into categories that you can + filter and group. items: $ref: '#/components/schemas/AlertTag' type: array @@ -5872,8 +6210,11 @@ components: type: object properties: signal_ids: + description: List of alert `id`s. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array status: @@ -5899,6 +6240,7 @@ components: - query - status SetAlertTags: + description: Object with list of tags to add and remove. type: object properties: tags_to_add: diff --git a/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml index 3c6094cac8589..8397eef7a9d96 100644 --- a/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/docs/openapi/ess/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml @@ -508,9 +508,11 @@ components: - microsoft_defender_endpoint type: string AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array CaseIds: @@ -662,7 +664,22 @@ components: required: - parameters GetProcessesRouteRequestBody: - $ref: '#/components/schemas/NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids HostPathScriptParameters: type: object properties: @@ -681,7 +698,22 @@ components: required: - hostPath IsolateRouteRequestBody: - $ref: '#/components/schemas/NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids KillProcessRouteRequestBody: allOf: - type: object @@ -761,33 +793,6 @@ components: type: string required: - hostStatuses - NonEmptyString: - description: A string that does not contain only whitespace characters - format: nonempty - minLength: 1 - type: string - NoParametersRequestSchema: - type: object - properties: - body: - type: object - properties: - agent_type: - $ref: '#/components/schemas/AgentTypes' - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' - required: - - endpoint_ids - required: - - body Page: default: 1 description: Page number @@ -942,7 +947,22 @@ components: minLength: 1 type: array UnisolateRouteRequestBody: - $ref: '#/components/schemas/NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids UploadRouteRequestBody: allOf: - type: object diff --git a/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml index 4a1d204e8c967..48289ff323178 100644 --- a/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_detections_api_2023_10_31.bundled.schema.yaml @@ -32,6 +32,42 @@ paths: '200': content: application/json: + examples: + success: + value: + application: {} + cluster: + all: true + manage: true + manage_api_key: true + manage_index_templates: true + manage_ml: true + manage_own_api_key: true + manage_pipeline: true + manage_security: true + manage_transform: true + monitor: true + monitor_ml: true + monitor_transform: true + has_all_requested: true + has_encryption_key: true + index: + .alerts-security.alerts-default: + all: true + create: true + create_doc: true + create_index: true + delete: true + delete_index: true + index: true + maintenance: true + manage: true + monitor: true + read: true + view_index_metadata: true + write: true + is_authenticated: true + username: elastic schema: type: object properties: @@ -586,6 +622,25 @@ paths: requestBody: content: application/json: + examples: + add: + value: + assignees: + add: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + remove: [] + ids: + - >- + 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 + remove: + value: + assignees: + add: [] + remove: + - u_MxY0jbrft7EcfC6iNZSUGeI_n6iYrSwZj5mWF5EqmSU_0 + ids: + - >- + 681c2a707335aa7df5f349b70013d87254746191712ecf0ced9b3e2d538503a6 schema: type: object properties: @@ -594,13 +649,32 @@ paths: description: Details about the assignees to assign and unassign. ids: $ref: '#/components/schemas/AlertIds' - description: List of alerts ids to assign and unassign passed assignees. required: - assignees - ids required: true responses: '200': + content: + application/ndjson: + examples: + add: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + - bulk: 0, + - search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 76, + total: 1, + updated: 1, + version_conflicts: 0, description: Indicates a successful call. '400': description: Invalid request. @@ -614,6 +688,35 @@ paths: requestBody: content: application/json: + examples: + query: + value: + aggs: + alertsByGrouping: + terms: + field: host.name + size: 10 + missingFields: + missing: + field: host.name + query: + bool: + filter: + - bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + - range: + '@timestamp': + gte: 2025-01-17T08:00:00.000Z + lte: 2025-01-18T07:59:59.999Z + runtime_mappings: {} + size: 0 schema: description: Elasticsearch query and aggregation request type: object @@ -651,6 +754,31 @@ paths: '200': content: application/json: + examples: + success: + value: + _shards: + failed: 0 + skipped: 0 + successful: 1 + total: 1 + aggregations: + alertsByGrouping: + buckets: + - doc_count: 5 + key: Host-f43kkddfyc + doc_count_error_upper_bound: 0 + sum_other_doc_count: 0 + missingFields: + doc_count: 0 + hits: + hits: [] + max_score: null + total: + relation: eq + value: 5 + timed_out: false + took: 0 schema: additionalProperties: true description: Elasticsearch search response @@ -687,6 +815,44 @@ paths: requestBody: content: application/json: + examples: + byId: + value: + signal_ids: + - >- + 80e1383f856e67c1b7f7a1634744fa6d66b6e2ef7aa26d226e57afb5a7b2b4a1 + status: closed + byQuery: + value: + conflicts: proceed + query: + bool: + filter: + - '@timestamp': + format: strict_date_optional_time + gte: 2024-10-23T07:00:00.000Z + lte: 2025-01-21T20:12:11.704Z + range: null + - bool: + filter: + bool: + filter: + - match_phrase: + kibana.alert.workflow_status: open + - '@timestamp': + format: strict_date_optional_time + gte: 2024-10-23T07:00:00.000Z + lte: 2025-01-21T20:12:11.704Z + range: null + must: [] + must_not: + - exists: + field: kibana.alert.building_block_type + should: [] + must: [] + must_not: [] + should: [] + status: closed schema: oneOf: - $ref: '#/components/schemas/SetAlertsStatusByIds' @@ -699,6 +865,41 @@ paths: '200': content: application/json: + examples: + byId: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 81 + total: 1 + updated: 1 + version_conflicts: 0 + byQuery: + value: + batches: 1 + deleted: 0 + failures: [] + noops: 0 + requests_per_second: -1 + retries: + bulk: 0 + search: 0 + throttled_millis: 0 + throttled_until_millis: 0 + timed_out: false + took: 100 + total: 17 + updated: 17 + version_conflicts: 0 schema: additionalProperties: true description: Elasticsearch update by query response @@ -738,6 +939,25 @@ paths: requestBody: content: application/json: + examples: + add: + value: + ids: + - >- + 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: + - Duplicate + tags_to_remove: [] + remove: + value: + ids: + - >- + 549c7129c76cbd554aba1bd638f8a49dde95088f5832e50218358e7eca1cf16e + tags: + tags_to_add: [] + tags_to_remove: + - Duplicate schema: type: object properties: @@ -756,6 +976,24 @@ paths: '200': content: application/json: + examples: + success: + value: + batches: 1, + deleted: 0, + failures: [] + noops: 0, + requests_per_second: '-1,' + retries: + bulk: 0, + search: 0 + throttled_millis: 0, + throttled_until_millis: 0, + timed_out: false, + took: 68, + total: 1, + updated: 1, + version_conflicts: 0, schema: additionalProperties: true description: Elasticsearch update by query response @@ -806,22 +1044,28 @@ components: type: object properties: add: - description: A list of users ids to assign. items: - $ref: '#/components/schemas/NonEmptyString' + description: A list of users ids to assign. + format: nonempty + minLength: 1 + type: string type: array remove: - description: A list of users ids to unassign. items: - $ref: '#/components/schemas/NonEmptyString' + description: A list of users ids to unassign. + format: nonempty + minLength: 1 + type: string type: array required: - add - remove AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array AlertsIndex: @@ -843,6 +1087,9 @@ components: - additionalProperties: true type: object AlertStatus: + description: >- + The status of an alert, which can be `open`, `acknowledged`, + `in-progress`, or `closed`. enum: - open - closed @@ -896,8 +1143,16 @@ components: - suppress type: string AlertTag: - $ref: '#/components/schemas/NonEmptyString' + description: >- + Use alert tags to organize related alerts into categories that you can + filter and group. + format: nonempty + minLength: 1 + type: string AlertTags: + description: >- + List of keywords to organize related alerts into categories that you can + filter and group. items: $ref: '#/components/schemas/AlertTag' type: array @@ -5021,8 +5276,11 @@ components: type: object properties: signal_ids: + description: List of alert `id`s. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array status: @@ -5048,6 +5306,7 @@ components: - query - status SetAlertTags: + description: Object with list of tags to add and remove. type: object properties: tags_to_add: diff --git a/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml b/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml index 6604fce5a4697..0e0ecfbc94ddb 100644 --- a/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/solutions/security/plugins/security_solution/docs/openapi/serverless/security_solution_endpoint_management_api_2023_10_31.bundled.schema.yaml @@ -508,9 +508,11 @@ components: - microsoft_defender_endpoint type: string AlertIds: - description: A list of alerts ids. + description: A list of alerts `id`s. items: - $ref: '#/components/schemas/NonEmptyString' + format: nonempty + minLength: 1 + type: string minItems: 1 type: array CaseIds: @@ -662,7 +664,22 @@ components: required: - parameters GetProcessesRouteRequestBody: - $ref: '#/components/schemas/NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids HostPathScriptParameters: type: object properties: @@ -681,7 +698,22 @@ components: required: - hostPath IsolateRouteRequestBody: - $ref: '#/components/schemas/NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids KillProcessRouteRequestBody: allOf: - type: object @@ -761,33 +793,6 @@ components: type: string required: - hostStatuses - NonEmptyString: - description: A string that does not contain only whitespace characters - format: nonempty - minLength: 1 - type: string - NoParametersRequestSchema: - type: object - properties: - body: - type: object - properties: - agent_type: - $ref: '#/components/schemas/AgentTypes' - alert_ids: - $ref: '#/components/schemas/AlertIds' - case_ids: - $ref: '#/components/schemas/CaseIds' - comment: - $ref: '#/components/schemas/Comment' - endpoint_ids: - $ref: '#/components/schemas/EndpointIds' - parameters: - $ref: '#/components/schemas/Parameters' - required: - - endpoint_ids - required: - - body Page: default: 1 description: Page number @@ -942,7 +947,22 @@ components: minLength: 1 type: array UnisolateRouteRequestBody: - $ref: '#/components/schemas/NoParametersRequestSchema' + type: object + properties: + agent_type: + $ref: '#/components/schemas/AgentTypes' + alert_ids: + $ref: '#/components/schemas/AlertIds' + case_ids: + $ref: '#/components/schemas/CaseIds' + comment: + $ref: '#/components/schemas/Comment' + endpoint_ids: + $ref: '#/components/schemas/EndpointIds' + parameters: + $ref: '#/components/schemas/Parameters' + required: + - endpoint_ids UploadRouteRequestBody: allOf: - type: object diff --git a/x-pack/solutions/security/plugins/security_solution/public/assistant/get_comments/content_reference/content_reference_parser.ts b/x-pack/solutions/security/plugins/security_solution/public/assistant/get_comments/content_reference/content_reference_parser.ts index 74ff7803ce3e0..247cd10fb45c4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/assistant/get_comments/content_reference/content_reference_parser.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/assistant/get_comments/content_reference/content_reference_parser.ts @@ -69,7 +69,7 @@ export const ContentReferenceParser: Plugin = function ContentReferenceParser() const contentReferenceId = readArg('(', ')'); - const closeChar = value[index++]; + const closeChar = value[index]; if (closeChar !== '}') return false; const now = eat.now(); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx index ec1deafdf771f..ae359d8e65cb5 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/mock/mock_timelines_plugin.tsx @@ -16,7 +16,6 @@ const useAddToTimeline = () => ({ export const mockTimelines = { getLastUpdated: jest.fn(), - getLoadingPanel: jest.fn(), getFieldBrowser: jest.fn(), getUseAddToTimeline: () => useAddToTimeline, }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/mock/storybook_providers.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/mock/storybook_providers.tsx index 1f5a7c43b38b1..7a1a97e9aa557 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/mock/storybook_providers.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/mock/storybook_providers.tsx @@ -79,11 +79,9 @@ const coreMock = { timelines: { getHoverActions: () => ({ getAddToTimelineButton: () => {}, - getColumnToggleButton: () => {}, getCopyButton: () => {}, getFilterForValueButton: () => {}, getFilterOutValueButton: () => {}, - getOverflowButton: () => {}, }), }, } as unknown as CoreStart; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/ml_jobs_description/ml_job_item.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/ml_jobs_description/ml_job_item.tsx index e32b8cb98270c..edb181ff9ee1c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/ml_jobs_description/ml_job_item.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation/components/ml_jobs_description/ml_job_item.tsx @@ -7,9 +7,8 @@ import type { FC, ReactNode } from 'react'; import React, { memo } from 'react'; -import styled from 'styled-components'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { euiThemeVars } from '@kbn/ui-theme'; +import { css } from '@emotion/react'; +import { EuiFlexGroup, EuiFlexItem, useEuiTheme } from '@elastic/eui'; import type { MlSummaryJob } from '@kbn/ml-plugin/public'; import * as i18n from './translations'; @@ -20,19 +19,19 @@ import { MlJobLink } from '../ml_job_link/ml_job_link'; import { MlAuditIcon } from '../ml_audit_icon'; import { MlJobStatusBadge } from '../ml_job_status_badge'; -const Wrapper = styled.div` - overflow: hidden; - margin-bottom: ${euiThemeVars.euiSizeS}; -`; - const MlJobItemComponent: FC<{ job: MlSummaryJob; switchComponent: ReactNode; }> = ({ job, switchComponent, ...props }) => { const isStarted = isJobStarted(job.jobState, job.datafeedState); + const { euiTheme } = useEuiTheme(); + const containerStyles = css` + overflow: hidden; + margin-bottom: ${euiTheme.size.s}; + `; return ( - +
@@ -46,7 +45,7 @@ const MlJobItemComponent: FC<{ {isStarted ? i18n.ML_STOP_JOB_LABEL : i18n.ML_RUN_JOB_LABEL} - +
); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar_field/query_field.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar_field/query_field.tsx index 44dad72b09c49..5694b7b432c85 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar_field/query_field.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/query_bar_field/query_field.tsx @@ -175,8 +175,8 @@ export const QueryBarField = ({ // if saved query fetched, reset values in queryBar input and filters to saved query's values useEffect(() => { if (resetToSavedQuery && savedQuery) { - const newFiledValue = savedQueryToFieldValue(savedQuery); - setFieldValue(newFiledValue); + const newFieldValue = savedQueryToFieldValue(savedQuery); + setFieldValue(newFieldValue); } }, [resetToSavedQuery, savedQuery, setFieldValue]); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/index.tsx index 26c12b2b6a88c..7e819565c9a6e 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/index.tsx @@ -12,6 +12,7 @@ import type { FieldHook } from '../../../../shared_imports'; import { Field } from '../../../../shared_imports'; import { THRESHOLD_FIELD_PLACEHOLDER } from './translations'; import * as styles from './styles'; +import { FieldSectionGroup } from './styles'; export interface FieldValueThreshold { field: string[]; @@ -66,7 +67,7 @@ const ThresholdInputComponent: React.FC = ({ return ( - + = ({ type={thresholdValue.type} /> - - + + = ({ type={thresholdCardinalityValue.type} /> - + ); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/styles.ts b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/styles.ts index f37b6a5816272..31b64389a83bd 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/styles.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_creation_ui/components/threshold_input/styles.ts @@ -5,8 +5,9 @@ * 2.0. */ +import { EuiFlexGroup } from '@elastic/eui'; import { css, cx } from '@emotion/css'; -import { euiThemeVars } from '@kbn/ui-theme'; +import styled from '@emotion/styled'; const CONTAINER_BREAKPOINT = 500; @@ -35,7 +36,7 @@ export const operatorContainer = cx( text-align: center; @container (min-width: ${CONTAINER_BREAKPOINT}px) { - margin-top: ${euiThemeVars.euiSizeXL}; + align-self: center; justify-content: flex-start; flex: 0 0 auto; } @@ -51,12 +52,12 @@ export const input = cx( ` ); -export const fieldSection = css` - gap: ${euiThemeVars.euiSizeS}; +export const FieldSectionGroup = styled(EuiFlexGroup)` + gap: ${({ theme }) => theme.euiTheme.size.s}; flex-wrap: wrap; @container (min-width: ${CONTAINER_BREAKPOINT}px) { flex-wrap: nowrap; - gap: ${euiThemeVars.euiSizeL}; + gap: ${({ theme }) => theme.euiTheme.size.l}; } `; diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx index 069cbce94cdd5..af8041e1aa111 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_exceptions/components/exception_item_card/meta.tsx @@ -49,6 +49,16 @@ export const ExceptionItemCardMetaInfo = memo( border-right: ${euiTheme.border.thin}; padding: ${euiTheme.size.xs} ${euiTheme.size.m} ${euiTheme.size.xs} 0; `; + const referenceLinksContainerStyles = css` + div { + color: ${euiTheme.colors.textPrimary}; + padding: ${euiTheme.size.s}; + + &:not(:last-child) { + border-bottom: ${euiTheme.border.thin}; + } + } + `; const isExpired = useMemo( () => (item.expire_time ? new Date(item.expire_time) <= new Date() : false), [item] @@ -91,11 +101,18 @@ export const ExceptionItemCardMetaInfo = memo( data-test-subj={`${dataTestSubj}-rulesPopover`} id={'rulesPopover'} > - + ); - }, [listAndReferences, metaInfoItemStyles, dataTestSubj, isRulesPopoverOpen, itemActions]); + }, [ + listAndReferences, + metaInfoItemStyles, + dataTestSubj, + isRulesPopoverOpen, + referenceLinksContainerStyles, + itemActions, + ]); const listsAffected = useMemo((): JSX.Element => { if (listAndReferences == null) return <>; @@ -121,6 +138,7 @@ export const ExceptionItemCardMetaInfo = memo( > ( } else { return <>; } - }, [listAndReferences, dataTestSubj, isListsPopoverOpen]); + }, [listAndReferences, dataTestSubj, isListsPopoverOpen, referenceLinksContainerStyles]); return ( { + if (editing) { + setFieldEditing(fieldName); + } else { + setFieldReadonly(fieldName); + } + }, [setFieldEditing, setFieldReadonly, editing, fieldName]); + invariant(fieldDiff, `Field diff is not found for ${fieldName}.`); const finalDiffableRule = calcFinalDiffableRule(ruleUpgradeState); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/rule_upgrade/rule_upgrade.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/rule_upgrade/rule_upgrade.tsx index 8aa6c74752a2b..1ef063ecb0df2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/rule_upgrade/rule_upgrade.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/three_way_diff/rule_upgrade/rule_upgrade.tsx @@ -7,6 +7,7 @@ import React, { memo } from 'react'; import { EuiSpacer } from '@elastic/eui'; +import type { FieldsUpgradeState } from '../../../../model/prebuilt_rule_upgrade'; import { FieldUpgradeStateEnum, type RuleUpgradeState, @@ -31,9 +32,7 @@ export const RuleUpgrade = memo(function RuleUpgrade({ const numOfFieldsWithUpdates = calcNumOfFieldsWithUpdates(ruleUpgradeState); const numOfSolvableConflicts = calcNumOfSolvableConflicts(ruleUpgradeState); const numOfNonSolvableConflicts = calcNumOfNonSolvableConflicts(ruleUpgradeState); - const fieldNames = Object.keys( - ruleUpgradeState.fieldsUpgradeState - ) as UpgradeableDiffableFields[]; + const fieldNames = extractSortedFieldNames(ruleUpgradeState.fieldsUpgradeState); return ( <> @@ -86,3 +85,30 @@ function calcNumOfNonSolvableConflicts(ruleUpgradeState: RuleUpgradeState): numb ({ state }) => state === FieldUpgradeStateEnum.NonSolvableConflict ).length; } + +/** + * Defines fields sorting order by state. + * Lower number corresponds to higher priority. + */ +const FIELDS_STATE_ORDER_MAP = { + [FieldUpgradeStateEnum.NonSolvableConflict]: 0, + [FieldUpgradeStateEnum.SolvableConflict]: 1, + [FieldUpgradeStateEnum.SameUpdate]: 2, + [FieldUpgradeStateEnum.NoConflict]: 3, + [FieldUpgradeStateEnum.Accepted]: 4, + [FieldUpgradeStateEnum.NoUpdate]: 5, +} as const; + +function extractSortedFieldNames( + fieldsUpgradeState: FieldsUpgradeState +): UpgradeableDiffableFields[] { + const fieldNames = Object.keys(fieldsUpgradeState) as UpgradeableDiffableFields[]; + + fieldNames.sort( + (a, b) => + FIELDS_STATE_ORDER_MAP[fieldsUpgradeState[a].state] - + FIELDS_STATE_ORDER_MAP[fieldsUpgradeState[b].state] + ); + + return fieldNames; +} diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx index 6a127faacdac6..b4456715abd4b 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx @@ -38,13 +38,11 @@ export const AllRules = React.memo(() => { ); } else { return ( - <> - - - - - - + + + + + ); } }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/rule_preview_context.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/rule_preview_context.tsx new file mode 100644 index 0000000000000..3f0acd778117e --- /dev/null +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/rule_preview_context.tsx @@ -0,0 +1,70 @@ +/* + * Copyright 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 { invariant } from '@formatjs/intl-utils'; +import useSet from 'react-use/lib/useSet'; +import React, { createContext, useContext, useEffect, useMemo } from 'react'; +import usePrevious from 'react-use/lib/usePrevious'; + +export interface RulePreviewContextType { + /** + * Sets the rule is being edited in the rule upgrade flyout + */ + setFieldEditing: (fieldName: string) => void; + + /** + * Sets the rule is not being edited in the rule upgrade flyout + */ + setFieldReadonly: (fieldName: string) => void; + + /** + * Returns whether the rule is being edited in the rule upgrade flyout + */ + isEditingRule: boolean; +} + +const RulePreviewContext = createContext(null); + +interface RulePreviewContextProviderProps { + children: React.ReactNode; + ruleId: string | undefined; +} + +export function RulePreviewContextProvider({ children, ruleId }: RulePreviewContextProviderProps) { + const [editedFields, { add, remove, reset }] = useSet(); + const prevRuleId = usePrevious(ruleId); + + useEffect(() => { + if (ruleId !== prevRuleId) { + reset(); + } + }, [reset, ruleId, prevRuleId]); + + const isEditingRule = editedFields.size > 0; + + const contextValue: RulePreviewContextType = useMemo( + () => ({ + isEditingRule, + setFieldEditing: add, + setFieldReadonly: remove, + }), + [isEditingRule, add, remove] + ); + + return {children}; +} + +export function useRulePreviewContext() { + const context = useContext(RulePreviewContext); + + invariant( + context !== null, + 'useRulePreviewContext must be used inside a RulePreviewContextProvider' + ); + + return context; +} diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx index a52bcb1bac59e..8e2ce07ed8cf1 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx @@ -269,7 +269,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ [rulesUpgradeState] ); const ruleActionsFactory = useCallback( - (rule: RuleResponse, closeRulePreview: () => void) => { + (rule: RuleResponse, closeRulePreview: () => void, isEditingRule: boolean) => { const ruleUpgradeState = rulesUpgradeState[rule.rule_id]; if (!ruleUpgradeState) { return null; @@ -282,7 +282,8 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ loadingRules.includes(rule.rule_id) || isRefetching || isUpgradingSecurityPackages || - (ruleUpgradeState.hasUnresolvedConflicts && !hasRuleTypeChange) + (ruleUpgradeState.hasUnresolvedConflicts && !hasRuleTypeChange) || + isEditingRule } onClick={() => { if (hasRuleTypeChange || isRulesCustomizationEnabled === false) { diff --git a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rule_preview_flyout.tsx b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rule_preview_flyout.tsx index 0c16a5c6ed165..b7deac4b3b7eb 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rule_preview_flyout.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/use_rule_preview_flyout.tsx @@ -6,21 +6,31 @@ */ import type { ReactNode } from 'react'; -import React, { useCallback, useState, useMemo } from 'react'; +import React, { useCallback, useState, useMemo, memo } from 'react'; import type { EuiTabbedContentTab } from '@elastic/eui'; import { invariant } from '../../../../../common/utils/invariant'; import type { RuleSignatureId } from '../../../../../common/api/detection_engine'; import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import { RuleDetailsFlyout } from '../../../rule_management/components/rule_details/rule_details_flyout'; - -interface UseRulePreviewFlyoutParams { - rules: RuleResponse[]; - ruleActionsFactory: (rule: RuleResponse, closeRulePreview: () => void) => ReactNode; +import { + RulePreviewContextProvider, + useRulePreviewContext, +} from './upgrade_prebuilt_rules_table/rule_preview_context'; +interface UseRulePreviewFlyoutBaseParams { + ruleActionsFactory: ( + rule: RuleResponse, + closeRulePreview: () => void, + isEditingRule: boolean + ) => ReactNode; extraTabsFactory?: (rule: RuleResponse) => EuiTabbedContentTab[]; subHeaderFactory?: (rule: RuleResponse) => ReactNode; flyoutProps: RulePreviewFlyoutProps; } +interface UseRulePreviewFlyoutParams extends UseRulePreviewFlyoutBaseParams { + rules: RuleResponse[]; +} + interface RulePreviewFlyoutProps { /** * Rule preview flyout unique id used in HTML @@ -44,41 +54,76 @@ export function useRulePreviewFlyout({ }: UseRulePreviewFlyoutParams): UseRulePreviewFlyoutResult { const [rule, setRuleForPreview] = useState(); const closeRulePreview = useCallback(() => setRuleForPreview(undefined), []); - const subHeader = useMemo( - () => (rule ? subHeaderFactory?.(rule) : null), - [subHeaderFactory, rule] + const openRulePreview = useCallback( + (ruleId: RuleSignatureId) => { + const ruleToShowInFlyout = rules.find((x) => x.rule_id === ruleId); + + invariant(ruleToShowInFlyout, `Rule with rule_id ${ruleId} not found`); + setRuleForPreview(ruleToShowInFlyout); + }, + [rules, setRuleForPreview] ); + const rulePreviewFlyout = ( + + + + ); + + return { + rulePreviewFlyout, + openRulePreview, + closeRulePreview, + }; +} + +const RulePreviewFlyoutInternal = memo(function RulePreviewFlyoutInternal({ + rule, + closeRulePreview, + extraTabsFactory, + ruleActionsFactory, + subHeaderFactory, + flyoutProps, +}: UseRulePreviewFlyoutBaseParams & { + rule: RuleResponse | undefined; + closeRulePreview: () => void; +}) { + const { isEditingRule } = useRulePreviewContext(); + const ruleActions = useMemo( - () => rule && ruleActionsFactory(rule, closeRulePreview), - [rule, ruleActionsFactory, closeRulePreview] + () => rule && ruleActionsFactory(rule, closeRulePreview, isEditingRule), + [rule, ruleActionsFactory, closeRulePreview, isEditingRule] ); const extraTabs = useMemo( () => (rule && extraTabsFactory ? extraTabsFactory(rule) : []), [rule, extraTabsFactory] ); - return { - rulePreviewFlyout: rule && ( - - ), - openRulePreview: useCallback( - (ruleId: RuleSignatureId) => { - const ruleToShowInFlyout = rules.find((x) => x.rule_id === ruleId); + const subHeader = useMemo( + () => (rule ? subHeaderFactory?.(rule) : null), + [subHeaderFactory, rule] + ); - invariant(ruleToShowInFlyout, `Rule with rule_id ${ruleId} not found`); - setRuleForPreview(ruleToShowInFlyout); - }, - [rules, setRuleForPreview] - ), - closeRulePreview, - }; -} + if (!rule) { + return null; + } + + return ( + + ); +}); diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_progress_bar_panel/alerts_progress_bar.tsx b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_progress_bar_panel/alerts_progress_bar.tsx index 1776c9d5fc9cc..0c2b01c8864a0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_progress_bar_panel/alerts_progress_bar.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/components/alerts_kpis/alerts_progress_bar_panel/alerts_progress_bar.tsx @@ -5,18 +5,19 @@ * 2.0. */ import { - EuiProgress, - EuiSpacer, - EuiText, - EuiHorizontalRule, - EuiPopoverTitle, - EuiLink, + EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiHorizontalRule, + EuiLink, EuiPopover, - EuiButtonIcon, + EuiPopoverTitle, + EuiProgress, + EuiSpacer, + EuiText, + useEuiTheme, } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import styled from 'styled-components'; import { TableId } from '@kbn/securitysolution-data-table'; import type { AlertsProgressBarData, GroupBySelection } from './types'; @@ -45,6 +46,7 @@ const StyledEuiProgress = styled(EuiProgress)` const DataStatsWrapper = styled.div` width: 250px; `; + export interface AlertsProcessBarProps { data: AlertsProgressBarData[]; isLoading: boolean; @@ -58,6 +60,7 @@ export const AlertsProgressBar: React.FC = ({ addFilter, groupBySelection, }) => { + const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const onButtonClick = () => setIsPopoverOpen(!isPopoverOpen); const closePopover = () => setIsPopoverOpen(false); @@ -113,6 +116,14 @@ export const AlertsProgressBar: React.FC = ({ ); }; + const color = useMemo( + () => + euiTheme.themeName === 'EUI_THEME_BOREALIS' + ? euiTheme.colors.vis.euiColorVis6 + : euiTheme.colors.vis.euiColorVis9, + [euiTheme] + ); + return ( <> @@ -159,7 +170,7 @@ export const AlertsProgressBar: React.FC = ({ } max={1} - color={`vis9`} + color={color} size="s" value={item.percentage} label={ diff --git a/x-pack/solutions/security/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts b/x-pack/solutions/security/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts index 6966cc4b5a5fe..5d4b5cbdc7a24 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/detections/mitre/mitre_tactics_techniques.ts @@ -17,7 +17,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0009', name: 'Collection', - reference: 'https://attack.mitre.org/tactics/TA0009', + reference: 'https://attack.mitre.org/tactics/TA0009/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.collectionDescription', { defaultMessage: 'Collection (TA0009)' } @@ -27,7 +27,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0011', name: 'Command and Control', - reference: 'https://attack.mitre.org/tactics/TA0011', + reference: 'https://attack.mitre.org/tactics/TA0011/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.commandAndControlDescription', { defaultMessage: 'Command and Control (TA0011)' } @@ -37,7 +37,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0006', name: 'Credential Access', - reference: 'https://attack.mitre.org/tactics/TA0006', + reference: 'https://attack.mitre.org/tactics/TA0006/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.credentialAccessDescription', { defaultMessage: 'Credential Access (TA0006)' } @@ -47,7 +47,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0005', name: 'Defense Evasion', - reference: 'https://attack.mitre.org/tactics/TA0005', + reference: 'https://attack.mitre.org/tactics/TA0005/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.defenseEvasionDescription', { defaultMessage: 'Defense Evasion (TA0005)' } @@ -57,7 +57,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0007', name: 'Discovery', - reference: 'https://attack.mitre.org/tactics/TA0007', + reference: 'https://attack.mitre.org/tactics/TA0007/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.discoveryDescription', { defaultMessage: 'Discovery (TA0007)' } @@ -67,7 +67,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0002', name: 'Execution', - reference: 'https://attack.mitre.org/tactics/TA0002', + reference: 'https://attack.mitre.org/tactics/TA0002/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.executionDescription', { defaultMessage: 'Execution (TA0002)' } @@ -77,7 +77,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0010', name: 'Exfiltration', - reference: 'https://attack.mitre.org/tactics/TA0010', + reference: 'https://attack.mitre.org/tactics/TA0010/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.exfiltrationDescription', { defaultMessage: 'Exfiltration (TA0010)' } @@ -87,7 +87,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0040', name: 'Impact', - reference: 'https://attack.mitre.org/tactics/TA0040', + reference: 'https://attack.mitre.org/tactics/TA0040/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.impactDescription', { defaultMessage: 'Impact (TA0040)' } @@ -97,7 +97,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0001', name: 'Initial Access', - reference: 'https://attack.mitre.org/tactics/TA0001', + reference: 'https://attack.mitre.org/tactics/TA0001/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.initialAccessDescription', { defaultMessage: 'Initial Access (TA0001)' } @@ -107,7 +107,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0008', name: 'Lateral Movement', - reference: 'https://attack.mitre.org/tactics/TA0008', + reference: 'https://attack.mitre.org/tactics/TA0008/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.lateralMovementDescription', { defaultMessage: 'Lateral Movement (TA0008)' } @@ -117,7 +117,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0003', name: 'Persistence', - reference: 'https://attack.mitre.org/tactics/TA0003', + reference: 'https://attack.mitre.org/tactics/TA0003/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.persistenceDescription', { defaultMessage: 'Persistence (TA0003)' } @@ -127,7 +127,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0004', name: 'Privilege Escalation', - reference: 'https://attack.mitre.org/tactics/TA0004', + reference: 'https://attack.mitre.org/tactics/TA0004/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.privilegeEscalationDescription', { defaultMessage: 'Privilege Escalation (TA0004)' } @@ -137,7 +137,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0043', name: 'Reconnaissance', - reference: 'https://attack.mitre.org/tactics/TA0043', + reference: 'https://attack.mitre.org/tactics/TA0043/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.reconnaissanceDescription', { defaultMessage: 'Reconnaissance (TA0043)' } @@ -147,7 +147,7 @@ export const tactics: MitreTactic[] = [ { id: 'TA0042', name: 'Resource Development', - reference: 'https://attack.mitre.org/tactics/TA0042', + reference: 'https://attack.mitre.org/tactics/TA0042/', label: i18n.translate( 'xpack.securitySolution.detectionEngine.mitreAttackTactics.resourceDevelopmentDescription', { defaultMessage: 'Resource Development (TA0042)' } @@ -164,7 +164,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1548', name: 'Abuse Elevation Control Mechanism', - reference: 'https://attack.mitre.org/techniques/T1548', + reference: 'https://attack.mitre.org/techniques/T1548/', tactics: ['privilege-escalation', 'defense-evasion'], value: 'abuseElevationControlMechanism', }, @@ -175,7 +175,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1134', name: 'Access Token Manipulation', - reference: 'https://attack.mitre.org/techniques/T1134', + reference: 'https://attack.mitre.org/techniques/T1134/', tactics: ['defense-evasion', 'privilege-escalation'], value: 'accessTokenManipulation', }, @@ -186,7 +186,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1531', name: 'Account Access Removal', - reference: 'https://attack.mitre.org/techniques/T1531', + reference: 'https://attack.mitre.org/techniques/T1531/', tactics: ['impact'], value: 'accountAccessRemoval', }, @@ -197,7 +197,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1087', name: 'Account Discovery', - reference: 'https://attack.mitre.org/techniques/T1087', + reference: 'https://attack.mitre.org/techniques/T1087/', tactics: ['discovery'], value: 'accountDiscovery', }, @@ -208,7 +208,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1098', name: 'Account Manipulation', - reference: 'https://attack.mitre.org/techniques/T1098', + reference: 'https://attack.mitre.org/techniques/T1098/', tactics: ['persistence', 'privilege-escalation'], value: 'accountManipulation', }, @@ -219,7 +219,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1650', name: 'Acquire Access', - reference: 'https://attack.mitre.org/techniques/T1650', + reference: 'https://attack.mitre.org/techniques/T1650/', tactics: ['resource-development'], value: 'acquireAccess', }, @@ -230,7 +230,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1583', name: 'Acquire Infrastructure', - reference: 'https://attack.mitre.org/techniques/T1583', + reference: 'https://attack.mitre.org/techniques/T1583/', tactics: ['resource-development'], value: 'acquireInfrastructure', }, @@ -241,7 +241,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1595', name: 'Active Scanning', - reference: 'https://attack.mitre.org/techniques/T1595', + reference: 'https://attack.mitre.org/techniques/T1595/', tactics: ['reconnaissance'], value: 'activeScanning', }, @@ -252,7 +252,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1557', name: 'Adversary-in-the-Middle', - reference: 'https://attack.mitre.org/techniques/T1557', + reference: 'https://attack.mitre.org/techniques/T1557/', tactics: ['credential-access', 'collection'], value: 'adversaryInTheMiddle', }, @@ -263,7 +263,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1071', name: 'Application Layer Protocol', - reference: 'https://attack.mitre.org/techniques/T1071', + reference: 'https://attack.mitre.org/techniques/T1071/', tactics: ['command-and-control'], value: 'applicationLayerProtocol', }, @@ -274,7 +274,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1010', name: 'Application Window Discovery', - reference: 'https://attack.mitre.org/techniques/T1010', + reference: 'https://attack.mitre.org/techniques/T1010/', tactics: ['discovery'], value: 'applicationWindowDiscovery', }, @@ -285,7 +285,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1560', name: 'Archive Collected Data', - reference: 'https://attack.mitre.org/techniques/T1560', + reference: 'https://attack.mitre.org/techniques/T1560/', tactics: ['collection'], value: 'archiveCollectedData', }, @@ -296,7 +296,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1123', name: 'Audio Capture', - reference: 'https://attack.mitre.org/techniques/T1123', + reference: 'https://attack.mitre.org/techniques/T1123/', tactics: ['collection'], value: 'audioCapture', }, @@ -307,7 +307,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1119', name: 'Automated Collection', - reference: 'https://attack.mitre.org/techniques/T1119', + reference: 'https://attack.mitre.org/techniques/T1119/', tactics: ['collection'], value: 'automatedCollection', }, @@ -318,7 +318,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1020', name: 'Automated Exfiltration', - reference: 'https://attack.mitre.org/techniques/T1020', + reference: 'https://attack.mitre.org/techniques/T1020/', tactics: ['exfiltration'], value: 'automatedExfiltration', }, @@ -329,7 +329,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1197', name: 'BITS Jobs', - reference: 'https://attack.mitre.org/techniques/T1197', + reference: 'https://attack.mitre.org/techniques/T1197/', tactics: ['defense-evasion', 'persistence'], value: 'bitsJobs', }, @@ -340,7 +340,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1547', name: 'Boot or Logon Autostart Execution', - reference: 'https://attack.mitre.org/techniques/T1547', + reference: 'https://attack.mitre.org/techniques/T1547/', tactics: ['persistence', 'privilege-escalation'], value: 'bootOrLogonAutostartExecution', }, @@ -351,7 +351,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1037', name: 'Boot or Logon Initialization Scripts', - reference: 'https://attack.mitre.org/techniques/T1037', + reference: 'https://attack.mitre.org/techniques/T1037/', tactics: ['persistence', 'privilege-escalation'], value: 'bootOrLogonInitializationScripts', }, @@ -362,7 +362,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1176', name: 'Browser Extensions', - reference: 'https://attack.mitre.org/techniques/T1176', + reference: 'https://attack.mitre.org/techniques/T1176/', tactics: ['persistence'], value: 'browserExtensions', }, @@ -373,7 +373,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1217', name: 'Browser Information Discovery', - reference: 'https://attack.mitre.org/techniques/T1217', + reference: 'https://attack.mitre.org/techniques/T1217/', tactics: ['discovery'], value: 'browserInformationDiscovery', }, @@ -384,7 +384,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1185', name: 'Browser Session Hijacking', - reference: 'https://attack.mitre.org/techniques/T1185', + reference: 'https://attack.mitre.org/techniques/T1185/', tactics: ['collection'], value: 'browserSessionHijacking', }, @@ -395,7 +395,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1110', name: 'Brute Force', - reference: 'https://attack.mitre.org/techniques/T1110', + reference: 'https://attack.mitre.org/techniques/T1110/', tactics: ['credential-access'], value: 'bruteForce', }, @@ -406,7 +406,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1612', name: 'Build Image on Host', - reference: 'https://attack.mitre.org/techniques/T1612', + reference: 'https://attack.mitre.org/techniques/T1612/', tactics: ['defense-evasion'], value: 'buildImageOnHost', }, @@ -417,7 +417,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1115', name: 'Clipboard Data', - reference: 'https://attack.mitre.org/techniques/T1115', + reference: 'https://attack.mitre.org/techniques/T1115/', tactics: ['collection'], value: 'clipboardData', }, @@ -428,7 +428,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1651', name: 'Cloud Administration Command', - reference: 'https://attack.mitre.org/techniques/T1651', + reference: 'https://attack.mitre.org/techniques/T1651/', tactics: ['execution'], value: 'cloudAdministrationCommand', }, @@ -439,7 +439,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1580', name: 'Cloud Infrastructure Discovery', - reference: 'https://attack.mitre.org/techniques/T1580', + reference: 'https://attack.mitre.org/techniques/T1580/', tactics: ['discovery'], value: 'cloudInfrastructureDiscovery', }, @@ -450,7 +450,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1538', name: 'Cloud Service Dashboard', - reference: 'https://attack.mitre.org/techniques/T1538', + reference: 'https://attack.mitre.org/techniques/T1538/', tactics: ['discovery'], value: 'cloudServiceDashboard', }, @@ -461,7 +461,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1526', name: 'Cloud Service Discovery', - reference: 'https://attack.mitre.org/techniques/T1526', + reference: 'https://attack.mitre.org/techniques/T1526/', tactics: ['discovery'], value: 'cloudServiceDiscovery', }, @@ -472,7 +472,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1619', name: 'Cloud Storage Object Discovery', - reference: 'https://attack.mitre.org/techniques/T1619', + reference: 'https://attack.mitre.org/techniques/T1619/', tactics: ['discovery'], value: 'cloudStorageObjectDiscovery', }, @@ -483,7 +483,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1059', name: 'Command and Scripting Interpreter', - reference: 'https://attack.mitre.org/techniques/T1059', + reference: 'https://attack.mitre.org/techniques/T1059/', tactics: ['execution'], value: 'commandAndScriptingInterpreter', }, @@ -494,7 +494,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1092', name: 'Communication Through Removable Media', - reference: 'https://attack.mitre.org/techniques/T1092', + reference: 'https://attack.mitre.org/techniques/T1092/', tactics: ['command-and-control'], value: 'communicationThroughRemovableMedia', }, @@ -505,7 +505,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1586', name: 'Compromise Accounts', - reference: 'https://attack.mitre.org/techniques/T1586', + reference: 'https://attack.mitre.org/techniques/T1586/', tactics: ['resource-development'], value: 'compromiseAccounts', }, @@ -516,7 +516,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1554', name: 'Compromise Host Software Binary', - reference: 'https://attack.mitre.org/techniques/T1554', + reference: 'https://attack.mitre.org/techniques/T1554/', tactics: ['persistence'], value: 'compromiseHostSoftwareBinary', }, @@ -527,7 +527,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1584', name: 'Compromise Infrastructure', - reference: 'https://attack.mitre.org/techniques/T1584', + reference: 'https://attack.mitre.org/techniques/T1584/', tactics: ['resource-development'], value: 'compromiseInfrastructure', }, @@ -538,7 +538,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1609', name: 'Container Administration Command', - reference: 'https://attack.mitre.org/techniques/T1609', + reference: 'https://attack.mitre.org/techniques/T1609/', tactics: ['execution'], value: 'containerAdministrationCommand', }, @@ -549,7 +549,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1613', name: 'Container and Resource Discovery', - reference: 'https://attack.mitre.org/techniques/T1613', + reference: 'https://attack.mitre.org/techniques/T1613/', tactics: ['discovery'], value: 'containerAndResourceDiscovery', }, @@ -560,7 +560,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1659', name: 'Content Injection', - reference: 'https://attack.mitre.org/techniques/T1659', + reference: 'https://attack.mitre.org/techniques/T1659/', tactics: ['initial-access', 'command-and-control'], value: 'contentInjection', }, @@ -571,7 +571,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1136', name: 'Create Account', - reference: 'https://attack.mitre.org/techniques/T1136', + reference: 'https://attack.mitre.org/techniques/T1136/', tactics: ['persistence'], value: 'createAccount', }, @@ -582,7 +582,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1543', name: 'Create or Modify System Process', - reference: 'https://attack.mitre.org/techniques/T1543', + reference: 'https://attack.mitre.org/techniques/T1543/', tactics: ['persistence', 'privilege-escalation'], value: 'createOrModifySystemProcess', }, @@ -593,7 +593,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1555', name: 'Credentials from Password Stores', - reference: 'https://attack.mitre.org/techniques/T1555', + reference: 'https://attack.mitre.org/techniques/T1555/', tactics: ['credential-access'], value: 'credentialsFromPasswordStores', }, @@ -604,7 +604,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1485', name: 'Data Destruction', - reference: 'https://attack.mitre.org/techniques/T1485', + reference: 'https://attack.mitre.org/techniques/T1485/', tactics: ['impact'], value: 'dataDestruction', }, @@ -615,7 +615,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1132', name: 'Data Encoding', - reference: 'https://attack.mitre.org/techniques/T1132', + reference: 'https://attack.mitre.org/techniques/T1132/', tactics: ['command-and-control'], value: 'dataEncoding', }, @@ -626,7 +626,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1486', name: 'Data Encrypted for Impact', - reference: 'https://attack.mitre.org/techniques/T1486', + reference: 'https://attack.mitre.org/techniques/T1486/', tactics: ['impact'], value: 'dataEncryptedForImpact', }, @@ -637,7 +637,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1565', name: 'Data Manipulation', - reference: 'https://attack.mitre.org/techniques/T1565', + reference: 'https://attack.mitre.org/techniques/T1565/', tactics: ['impact'], value: 'dataManipulation', }, @@ -648,7 +648,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1001', name: 'Data Obfuscation', - reference: 'https://attack.mitre.org/techniques/T1001', + reference: 'https://attack.mitre.org/techniques/T1001/', tactics: ['command-and-control'], value: 'dataObfuscation', }, @@ -659,7 +659,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1074', name: 'Data Staged', - reference: 'https://attack.mitre.org/techniques/T1074', + reference: 'https://attack.mitre.org/techniques/T1074/', tactics: ['collection'], value: 'dataStaged', }, @@ -670,7 +670,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1030', name: 'Data Transfer Size Limits', - reference: 'https://attack.mitre.org/techniques/T1030', + reference: 'https://attack.mitre.org/techniques/T1030/', tactics: ['exfiltration'], value: 'dataTransferSizeLimits', }, @@ -681,7 +681,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1530', name: 'Data from Cloud Storage', - reference: 'https://attack.mitre.org/techniques/T1530', + reference: 'https://attack.mitre.org/techniques/T1530/', tactics: ['collection'], value: 'dataFromCloudStorage', }, @@ -692,7 +692,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1602', name: 'Data from Configuration Repository', - reference: 'https://attack.mitre.org/techniques/T1602', + reference: 'https://attack.mitre.org/techniques/T1602/', tactics: ['collection'], value: 'dataFromConfigurationRepository', }, @@ -703,7 +703,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1213', name: 'Data from Information Repositories', - reference: 'https://attack.mitre.org/techniques/T1213', + reference: 'https://attack.mitre.org/techniques/T1213/', tactics: ['collection'], value: 'dataFromInformationRepositories', }, @@ -714,7 +714,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1005', name: 'Data from Local System', - reference: 'https://attack.mitre.org/techniques/T1005', + reference: 'https://attack.mitre.org/techniques/T1005/', tactics: ['collection'], value: 'dataFromLocalSystem', }, @@ -725,7 +725,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1039', name: 'Data from Network Shared Drive', - reference: 'https://attack.mitre.org/techniques/T1039', + reference: 'https://attack.mitre.org/techniques/T1039/', tactics: ['collection'], value: 'dataFromNetworkSharedDrive', }, @@ -736,7 +736,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1025', name: 'Data from Removable Media', - reference: 'https://attack.mitre.org/techniques/T1025', + reference: 'https://attack.mitre.org/techniques/T1025/', tactics: ['collection'], value: 'dataFromRemovableMedia', }, @@ -747,7 +747,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1622', name: 'Debugger Evasion', - reference: 'https://attack.mitre.org/techniques/T1622', + reference: 'https://attack.mitre.org/techniques/T1622/', tactics: ['defense-evasion', 'discovery'], value: 'debuggerEvasion', }, @@ -758,7 +758,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1491', name: 'Defacement', - reference: 'https://attack.mitre.org/techniques/T1491', + reference: 'https://attack.mitre.org/techniques/T1491/', tactics: ['impact'], value: 'defacement', }, @@ -769,7 +769,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1140', name: 'Deobfuscate/Decode Files or Information', - reference: 'https://attack.mitre.org/techniques/T1140', + reference: 'https://attack.mitre.org/techniques/T1140/', tactics: ['defense-evasion'], value: 'deobfuscateDecodeFilesOrInformation', }, @@ -780,7 +780,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1610', name: 'Deploy Container', - reference: 'https://attack.mitre.org/techniques/T1610', + reference: 'https://attack.mitre.org/techniques/T1610/', tactics: ['defense-evasion', 'execution'], value: 'deployContainer', }, @@ -791,7 +791,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1587', name: 'Develop Capabilities', - reference: 'https://attack.mitre.org/techniques/T1587', + reference: 'https://attack.mitre.org/techniques/T1587/', tactics: ['resource-development'], value: 'developCapabilities', }, @@ -802,7 +802,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1652', name: 'Device Driver Discovery', - reference: 'https://attack.mitre.org/techniques/T1652', + reference: 'https://attack.mitre.org/techniques/T1652/', tactics: ['discovery'], value: 'deviceDriverDiscovery', }, @@ -813,7 +813,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1006', name: 'Direct Volume Access', - reference: 'https://attack.mitre.org/techniques/T1006', + reference: 'https://attack.mitre.org/techniques/T1006/', tactics: ['defense-evasion'], value: 'directVolumeAccess', }, @@ -824,7 +824,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1561', name: 'Disk Wipe', - reference: 'https://attack.mitre.org/techniques/T1561', + reference: 'https://attack.mitre.org/techniques/T1561/', tactics: ['impact'], value: 'diskWipe', }, @@ -835,7 +835,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1482', name: 'Domain Trust Discovery', - reference: 'https://attack.mitre.org/techniques/T1482', + reference: 'https://attack.mitre.org/techniques/T1482/', tactics: ['discovery'], value: 'domainTrustDiscovery', }, @@ -846,7 +846,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1484', name: 'Domain or Tenant Policy Modification', - reference: 'https://attack.mitre.org/techniques/T1484', + reference: 'https://attack.mitre.org/techniques/T1484/', tactics: ['defense-evasion', 'privilege-escalation'], value: 'domainOrTenantPolicyModification', }, @@ -857,7 +857,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1189', name: 'Drive-by Compromise', - reference: 'https://attack.mitre.org/techniques/T1189', + reference: 'https://attack.mitre.org/techniques/T1189/', tactics: ['initial-access'], value: 'driveByCompromise', }, @@ -868,7 +868,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1568', name: 'Dynamic Resolution', - reference: 'https://attack.mitre.org/techniques/T1568', + reference: 'https://attack.mitre.org/techniques/T1568/', tactics: ['command-and-control'], value: 'dynamicResolution', }, @@ -879,7 +879,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1114', name: 'Email Collection', - reference: 'https://attack.mitre.org/techniques/T1114', + reference: 'https://attack.mitre.org/techniques/T1114/', tactics: ['collection'], value: 'emailCollection', }, @@ -890,7 +890,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1573', name: 'Encrypted Channel', - reference: 'https://attack.mitre.org/techniques/T1573', + reference: 'https://attack.mitre.org/techniques/T1573/', tactics: ['command-and-control'], value: 'encryptedChannel', }, @@ -901,7 +901,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1499', name: 'Endpoint Denial of Service', - reference: 'https://attack.mitre.org/techniques/T1499', + reference: 'https://attack.mitre.org/techniques/T1499/', tactics: ['impact'], value: 'endpointDenialOfService', }, @@ -912,7 +912,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1611', name: 'Escape to Host', - reference: 'https://attack.mitre.org/techniques/T1611', + reference: 'https://attack.mitre.org/techniques/T1611/', tactics: ['privilege-escalation'], value: 'escapeToHost', }, @@ -923,7 +923,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1585', name: 'Establish Accounts', - reference: 'https://attack.mitre.org/techniques/T1585', + reference: 'https://attack.mitre.org/techniques/T1585/', tactics: ['resource-development'], value: 'establishAccounts', }, @@ -934,7 +934,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1546', name: 'Event Triggered Execution', - reference: 'https://attack.mitre.org/techniques/T1546', + reference: 'https://attack.mitre.org/techniques/T1546/', tactics: ['privilege-escalation', 'persistence'], value: 'eventTriggeredExecution', }, @@ -945,7 +945,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1480', name: 'Execution Guardrails', - reference: 'https://attack.mitre.org/techniques/T1480', + reference: 'https://attack.mitre.org/techniques/T1480/', tactics: ['defense-evasion'], value: 'executionGuardrails', }, @@ -956,7 +956,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1048', name: 'Exfiltration Over Alternative Protocol', - reference: 'https://attack.mitre.org/techniques/T1048', + reference: 'https://attack.mitre.org/techniques/T1048/', tactics: ['exfiltration'], value: 'exfiltrationOverAlternativeProtocol', }, @@ -967,7 +967,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1041', name: 'Exfiltration Over C2 Channel', - reference: 'https://attack.mitre.org/techniques/T1041', + reference: 'https://attack.mitre.org/techniques/T1041/', tactics: ['exfiltration'], value: 'exfiltrationOverC2Channel', }, @@ -978,7 +978,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1011', name: 'Exfiltration Over Other Network Medium', - reference: 'https://attack.mitre.org/techniques/T1011', + reference: 'https://attack.mitre.org/techniques/T1011/', tactics: ['exfiltration'], value: 'exfiltrationOverOtherNetworkMedium', }, @@ -989,7 +989,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1052', name: 'Exfiltration Over Physical Medium', - reference: 'https://attack.mitre.org/techniques/T1052', + reference: 'https://attack.mitre.org/techniques/T1052/', tactics: ['exfiltration'], value: 'exfiltrationOverPhysicalMedium', }, @@ -1000,7 +1000,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1567', name: 'Exfiltration Over Web Service', - reference: 'https://attack.mitre.org/techniques/T1567', + reference: 'https://attack.mitre.org/techniques/T1567/', tactics: ['exfiltration'], value: 'exfiltrationOverWebService', }, @@ -1011,7 +1011,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1190', name: 'Exploit Public-Facing Application', - reference: 'https://attack.mitre.org/techniques/T1190', + reference: 'https://attack.mitre.org/techniques/T1190/', tactics: ['initial-access'], value: 'exploitPublicFacingApplication', }, @@ -1022,7 +1022,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1203', name: 'Exploitation for Client Execution', - reference: 'https://attack.mitre.org/techniques/T1203', + reference: 'https://attack.mitre.org/techniques/T1203/', tactics: ['execution'], value: 'exploitationForClientExecution', }, @@ -1033,7 +1033,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1212', name: 'Exploitation for Credential Access', - reference: 'https://attack.mitre.org/techniques/T1212', + reference: 'https://attack.mitre.org/techniques/T1212/', tactics: ['credential-access'], value: 'exploitationForCredentialAccess', }, @@ -1044,7 +1044,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1211', name: 'Exploitation for Defense Evasion', - reference: 'https://attack.mitre.org/techniques/T1211', + reference: 'https://attack.mitre.org/techniques/T1211/', tactics: ['defense-evasion'], value: 'exploitationForDefenseEvasion', }, @@ -1055,7 +1055,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1068', name: 'Exploitation for Privilege Escalation', - reference: 'https://attack.mitre.org/techniques/T1068', + reference: 'https://attack.mitre.org/techniques/T1068/', tactics: ['privilege-escalation'], value: 'exploitationForPrivilegeEscalation', }, @@ -1066,7 +1066,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1210', name: 'Exploitation of Remote Services', - reference: 'https://attack.mitre.org/techniques/T1210', + reference: 'https://attack.mitre.org/techniques/T1210/', tactics: ['lateral-movement'], value: 'exploitationOfRemoteServices', }, @@ -1077,7 +1077,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1133', name: 'External Remote Services', - reference: 'https://attack.mitre.org/techniques/T1133', + reference: 'https://attack.mitre.org/techniques/T1133/', tactics: ['persistence', 'initial-access'], value: 'externalRemoteServices', }, @@ -1088,7 +1088,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1008', name: 'Fallback Channels', - reference: 'https://attack.mitre.org/techniques/T1008', + reference: 'https://attack.mitre.org/techniques/T1008/', tactics: ['command-and-control'], value: 'fallbackChannels', }, @@ -1099,7 +1099,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1083', name: 'File and Directory Discovery', - reference: 'https://attack.mitre.org/techniques/T1083', + reference: 'https://attack.mitre.org/techniques/T1083/', tactics: ['discovery'], value: 'fileAndDirectoryDiscovery', }, @@ -1110,7 +1110,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1222', name: 'File and Directory Permissions Modification', - reference: 'https://attack.mitre.org/techniques/T1222', + reference: 'https://attack.mitre.org/techniques/T1222/', tactics: ['defense-evasion'], value: 'fileAndDirectoryPermissionsModification', }, @@ -1121,7 +1121,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1657', name: 'Financial Theft', - reference: 'https://attack.mitre.org/techniques/T1657', + reference: 'https://attack.mitre.org/techniques/T1657/', tactics: ['impact'], value: 'financialTheft', }, @@ -1132,7 +1132,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1495', name: 'Firmware Corruption', - reference: 'https://attack.mitre.org/techniques/T1495', + reference: 'https://attack.mitre.org/techniques/T1495/', tactics: ['impact'], value: 'firmwareCorruption', }, @@ -1143,7 +1143,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1187', name: 'Forced Authentication', - reference: 'https://attack.mitre.org/techniques/T1187', + reference: 'https://attack.mitre.org/techniques/T1187/', tactics: ['credential-access'], value: 'forcedAuthentication', }, @@ -1154,7 +1154,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1606', name: 'Forge Web Credentials', - reference: 'https://attack.mitre.org/techniques/T1606', + reference: 'https://attack.mitre.org/techniques/T1606/', tactics: ['credential-access'], value: 'forgeWebCredentials', }, @@ -1165,7 +1165,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1592', name: 'Gather Victim Host Information', - reference: 'https://attack.mitre.org/techniques/T1592', + reference: 'https://attack.mitre.org/techniques/T1592/', tactics: ['reconnaissance'], value: 'gatherVictimHostInformation', }, @@ -1176,7 +1176,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1589', name: 'Gather Victim Identity Information', - reference: 'https://attack.mitre.org/techniques/T1589', + reference: 'https://attack.mitre.org/techniques/T1589/', tactics: ['reconnaissance'], value: 'gatherVictimIdentityInformation', }, @@ -1187,7 +1187,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1590', name: 'Gather Victim Network Information', - reference: 'https://attack.mitre.org/techniques/T1590', + reference: 'https://attack.mitre.org/techniques/T1590/', tactics: ['reconnaissance'], value: 'gatherVictimNetworkInformation', }, @@ -1198,7 +1198,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1591', name: 'Gather Victim Org Information', - reference: 'https://attack.mitre.org/techniques/T1591', + reference: 'https://attack.mitre.org/techniques/T1591/', tactics: ['reconnaissance'], value: 'gatherVictimOrgInformation', }, @@ -1209,7 +1209,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1615', name: 'Group Policy Discovery', - reference: 'https://attack.mitre.org/techniques/T1615', + reference: 'https://attack.mitre.org/techniques/T1615/', tactics: ['discovery'], value: 'groupPolicyDiscovery', }, @@ -1220,7 +1220,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1200', name: 'Hardware Additions', - reference: 'https://attack.mitre.org/techniques/T1200', + reference: 'https://attack.mitre.org/techniques/T1200/', tactics: ['initial-access'], value: 'hardwareAdditions', }, @@ -1231,7 +1231,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1564', name: 'Hide Artifacts', - reference: 'https://attack.mitre.org/techniques/T1564', + reference: 'https://attack.mitre.org/techniques/T1564/', tactics: ['defense-evasion'], value: 'hideArtifacts', }, @@ -1242,7 +1242,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1665', name: 'Hide Infrastructure', - reference: 'https://attack.mitre.org/techniques/T1665', + reference: 'https://attack.mitre.org/techniques/T1665/', tactics: ['command-and-control'], value: 'hideInfrastructure', }, @@ -1253,7 +1253,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1574', name: 'Hijack Execution Flow', - reference: 'https://attack.mitre.org/techniques/T1574', + reference: 'https://attack.mitre.org/techniques/T1574/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], value: 'hijackExecutionFlow', }, @@ -1264,7 +1264,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1562', name: 'Impair Defenses', - reference: 'https://attack.mitre.org/techniques/T1562', + reference: 'https://attack.mitre.org/techniques/T1562/', tactics: ['defense-evasion'], value: 'impairDefenses', }, @@ -1275,7 +1275,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1656', name: 'Impersonation', - reference: 'https://attack.mitre.org/techniques/T1656', + reference: 'https://attack.mitre.org/techniques/T1656/', tactics: ['defense-evasion'], value: 'impersonation', }, @@ -1286,7 +1286,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1525', name: 'Implant Internal Image', - reference: 'https://attack.mitre.org/techniques/T1525', + reference: 'https://attack.mitre.org/techniques/T1525/', tactics: ['persistence'], value: 'implantInternalImage', }, @@ -1297,7 +1297,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1070', name: 'Indicator Removal', - reference: 'https://attack.mitre.org/techniques/T1070', + reference: 'https://attack.mitre.org/techniques/T1070/', tactics: ['defense-evasion'], value: 'indicatorRemoval', }, @@ -1308,7 +1308,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1202', name: 'Indirect Command Execution', - reference: 'https://attack.mitre.org/techniques/T1202', + reference: 'https://attack.mitre.org/techniques/T1202/', tactics: ['defense-evasion'], value: 'indirectCommandExecution', }, @@ -1319,7 +1319,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1105', name: 'Ingress Tool Transfer', - reference: 'https://attack.mitre.org/techniques/T1105', + reference: 'https://attack.mitre.org/techniques/T1105/', tactics: ['command-and-control'], value: 'ingressToolTransfer', }, @@ -1330,7 +1330,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1490', name: 'Inhibit System Recovery', - reference: 'https://attack.mitre.org/techniques/T1490', + reference: 'https://attack.mitre.org/techniques/T1490/', tactics: ['impact'], value: 'inhibitSystemRecovery', }, @@ -1341,7 +1341,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1056', name: 'Input Capture', - reference: 'https://attack.mitre.org/techniques/T1056', + reference: 'https://attack.mitre.org/techniques/T1056/', tactics: ['collection', 'credential-access'], value: 'inputCapture', }, @@ -1352,7 +1352,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1559', name: 'Inter-Process Communication', - reference: 'https://attack.mitre.org/techniques/T1559', + reference: 'https://attack.mitre.org/techniques/T1559/', tactics: ['execution'], value: 'interProcessCommunication', }, @@ -1363,7 +1363,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1534', name: 'Internal Spearphishing', - reference: 'https://attack.mitre.org/techniques/T1534', + reference: 'https://attack.mitre.org/techniques/T1534/', tactics: ['lateral-movement'], value: 'internalSpearphishing', }, @@ -1374,7 +1374,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1570', name: 'Lateral Tool Transfer', - reference: 'https://attack.mitre.org/techniques/T1570', + reference: 'https://attack.mitre.org/techniques/T1570/', tactics: ['lateral-movement'], value: 'lateralToolTransfer', }, @@ -1385,7 +1385,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1654', name: 'Log Enumeration', - reference: 'https://attack.mitre.org/techniques/T1654', + reference: 'https://attack.mitre.org/techniques/T1654/', tactics: ['discovery'], value: 'logEnumeration', }, @@ -1396,7 +1396,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1036', name: 'Masquerading', - reference: 'https://attack.mitre.org/techniques/T1036', + reference: 'https://attack.mitre.org/techniques/T1036/', tactics: ['defense-evasion'], value: 'masquerading', }, @@ -1407,7 +1407,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1556', name: 'Modify Authentication Process', - reference: 'https://attack.mitre.org/techniques/T1556', + reference: 'https://attack.mitre.org/techniques/T1556/', tactics: ['credential-access', 'defense-evasion', 'persistence'], value: 'modifyAuthenticationProcess', }, @@ -1418,7 +1418,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1578', name: 'Modify Cloud Compute Infrastructure', - reference: 'https://attack.mitre.org/techniques/T1578', + reference: 'https://attack.mitre.org/techniques/T1578/', tactics: ['defense-evasion'], value: 'modifyCloudComputeInfrastructure', }, @@ -1429,7 +1429,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1112', name: 'Modify Registry', - reference: 'https://attack.mitre.org/techniques/T1112', + reference: 'https://attack.mitre.org/techniques/T1112/', tactics: ['defense-evasion'], value: 'modifyRegistry', }, @@ -1440,7 +1440,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1601', name: 'Modify System Image', - reference: 'https://attack.mitre.org/techniques/T1601', + reference: 'https://attack.mitre.org/techniques/T1601/', tactics: ['defense-evasion'], value: 'modifySystemImage', }, @@ -1451,7 +1451,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1111', name: 'Multi-Factor Authentication Interception', - reference: 'https://attack.mitre.org/techniques/T1111', + reference: 'https://attack.mitre.org/techniques/T1111/', tactics: ['credential-access'], value: 'multiFactorAuthenticationInterception', }, @@ -1462,7 +1462,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1621', name: 'Multi-Factor Authentication Request Generation', - reference: 'https://attack.mitre.org/techniques/T1621', + reference: 'https://attack.mitre.org/techniques/T1621/', tactics: ['credential-access'], value: 'multiFactorAuthenticationRequestGeneration', }, @@ -1473,7 +1473,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1104', name: 'Multi-Stage Channels', - reference: 'https://attack.mitre.org/techniques/T1104', + reference: 'https://attack.mitre.org/techniques/T1104/', tactics: ['command-and-control'], value: 'multiStageChannels', }, @@ -1484,7 +1484,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1106', name: 'Native API', - reference: 'https://attack.mitre.org/techniques/T1106', + reference: 'https://attack.mitre.org/techniques/T1106/', tactics: ['execution'], value: 'nativeApi', }, @@ -1495,7 +1495,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1599', name: 'Network Boundary Bridging', - reference: 'https://attack.mitre.org/techniques/T1599', + reference: 'https://attack.mitre.org/techniques/T1599/', tactics: ['defense-evasion'], value: 'networkBoundaryBridging', }, @@ -1506,7 +1506,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1498', name: 'Network Denial of Service', - reference: 'https://attack.mitre.org/techniques/T1498', + reference: 'https://attack.mitre.org/techniques/T1498/', tactics: ['impact'], value: 'networkDenialOfService', }, @@ -1517,7 +1517,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1046', name: 'Network Service Discovery', - reference: 'https://attack.mitre.org/techniques/T1046', + reference: 'https://attack.mitre.org/techniques/T1046/', tactics: ['discovery'], value: 'networkServiceDiscovery', }, @@ -1528,7 +1528,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1135', name: 'Network Share Discovery', - reference: 'https://attack.mitre.org/techniques/T1135', + reference: 'https://attack.mitre.org/techniques/T1135/', tactics: ['discovery'], value: 'networkShareDiscovery', }, @@ -1539,7 +1539,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1040', name: 'Network Sniffing', - reference: 'https://attack.mitre.org/techniques/T1040', + reference: 'https://attack.mitre.org/techniques/T1040/', tactics: ['credential-access', 'discovery'], value: 'networkSniffing', }, @@ -1550,7 +1550,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1095', name: 'Non-Application Layer Protocol', - reference: 'https://attack.mitre.org/techniques/T1095', + reference: 'https://attack.mitre.org/techniques/T1095/', tactics: ['command-and-control'], value: 'nonApplicationLayerProtocol', }, @@ -1561,7 +1561,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1571', name: 'Non-Standard Port', - reference: 'https://attack.mitre.org/techniques/T1571', + reference: 'https://attack.mitre.org/techniques/T1571/', tactics: ['command-and-control'], value: 'nonStandardPort', }, @@ -1572,7 +1572,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1003', name: 'OS Credential Dumping', - reference: 'https://attack.mitre.org/techniques/T1003', + reference: 'https://attack.mitre.org/techniques/T1003/', tactics: ['credential-access'], value: 'osCredentialDumping', }, @@ -1583,7 +1583,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1027', name: 'Obfuscated Files or Information', - reference: 'https://attack.mitre.org/techniques/T1027', + reference: 'https://attack.mitre.org/techniques/T1027/', tactics: ['defense-evasion'], value: 'obfuscatedFilesOrInformation', }, @@ -1594,7 +1594,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1588', name: 'Obtain Capabilities', - reference: 'https://attack.mitre.org/techniques/T1588', + reference: 'https://attack.mitre.org/techniques/T1588/', tactics: ['resource-development'], value: 'obtainCapabilities', }, @@ -1605,7 +1605,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1137', name: 'Office Application Startup', - reference: 'https://attack.mitre.org/techniques/T1137', + reference: 'https://attack.mitre.org/techniques/T1137/', tactics: ['persistence'], value: 'officeApplicationStartup', }, @@ -1616,7 +1616,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1201', name: 'Password Policy Discovery', - reference: 'https://attack.mitre.org/techniques/T1201', + reference: 'https://attack.mitre.org/techniques/T1201/', tactics: ['discovery'], value: 'passwordPolicyDiscovery', }, @@ -1627,7 +1627,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1120', name: 'Peripheral Device Discovery', - reference: 'https://attack.mitre.org/techniques/T1120', + reference: 'https://attack.mitre.org/techniques/T1120/', tactics: ['discovery'], value: 'peripheralDeviceDiscovery', }, @@ -1638,7 +1638,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1069', name: 'Permission Groups Discovery', - reference: 'https://attack.mitre.org/techniques/T1069', + reference: 'https://attack.mitre.org/techniques/T1069/', tactics: ['discovery'], value: 'permissionGroupsDiscovery', }, @@ -1649,7 +1649,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1566', name: 'Phishing', - reference: 'https://attack.mitre.org/techniques/T1566', + reference: 'https://attack.mitre.org/techniques/T1566/', tactics: ['initial-access'], value: 'phishing', }, @@ -1660,7 +1660,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1598', name: 'Phishing for Information', - reference: 'https://attack.mitre.org/techniques/T1598', + reference: 'https://attack.mitre.org/techniques/T1598/', tactics: ['reconnaissance'], value: 'phishingForInformation', }, @@ -1671,7 +1671,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1647', name: 'Plist File Modification', - reference: 'https://attack.mitre.org/techniques/T1647', + reference: 'https://attack.mitre.org/techniques/T1647/', tactics: ['defense-evasion'], value: 'plistFileModification', }, @@ -1682,7 +1682,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1653', name: 'Power Settings', - reference: 'https://attack.mitre.org/techniques/T1653', + reference: 'https://attack.mitre.org/techniques/T1653/', tactics: ['persistence'], value: 'powerSettings', }, @@ -1693,7 +1693,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1542', name: 'Pre-OS Boot', - reference: 'https://attack.mitre.org/techniques/T1542', + reference: 'https://attack.mitre.org/techniques/T1542/', tactics: ['defense-evasion', 'persistence'], value: 'preOsBoot', }, @@ -1704,7 +1704,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1057', name: 'Process Discovery', - reference: 'https://attack.mitre.org/techniques/T1057', + reference: 'https://attack.mitre.org/techniques/T1057/', tactics: ['discovery'], value: 'processDiscovery', }, @@ -1715,7 +1715,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1055', name: 'Process Injection', - reference: 'https://attack.mitre.org/techniques/T1055', + reference: 'https://attack.mitre.org/techniques/T1055/', tactics: ['defense-evasion', 'privilege-escalation'], value: 'processInjection', }, @@ -1726,7 +1726,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1572', name: 'Protocol Tunneling', - reference: 'https://attack.mitre.org/techniques/T1572', + reference: 'https://attack.mitre.org/techniques/T1572/', tactics: ['command-and-control'], value: 'protocolTunneling', }, @@ -1737,7 +1737,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1090', name: 'Proxy', - reference: 'https://attack.mitre.org/techniques/T1090', + reference: 'https://attack.mitre.org/techniques/T1090/', tactics: ['command-and-control'], value: 'proxy', }, @@ -1748,7 +1748,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1012', name: 'Query Registry', - reference: 'https://attack.mitre.org/techniques/T1012', + reference: 'https://attack.mitre.org/techniques/T1012/', tactics: ['discovery'], value: 'queryRegistry', }, @@ -1759,7 +1759,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1620', name: 'Reflective Code Loading', - reference: 'https://attack.mitre.org/techniques/T1620', + reference: 'https://attack.mitre.org/techniques/T1620/', tactics: ['defense-evasion'], value: 'reflectiveCodeLoading', }, @@ -1770,7 +1770,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1219', name: 'Remote Access Software', - reference: 'https://attack.mitre.org/techniques/T1219', + reference: 'https://attack.mitre.org/techniques/T1219/', tactics: ['command-and-control'], value: 'remoteAccessSoftware', }, @@ -1781,7 +1781,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1563', name: 'Remote Service Session Hijacking', - reference: 'https://attack.mitre.org/techniques/T1563', + reference: 'https://attack.mitre.org/techniques/T1563/', tactics: ['lateral-movement'], value: 'remoteServiceSessionHijacking', }, @@ -1792,7 +1792,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1021', name: 'Remote Services', - reference: 'https://attack.mitre.org/techniques/T1021', + reference: 'https://attack.mitre.org/techniques/T1021/', tactics: ['lateral-movement'], value: 'remoteServices', }, @@ -1803,7 +1803,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1018', name: 'Remote System Discovery', - reference: 'https://attack.mitre.org/techniques/T1018', + reference: 'https://attack.mitre.org/techniques/T1018/', tactics: ['discovery'], value: 'remoteSystemDiscovery', }, @@ -1814,7 +1814,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1091', name: 'Replication Through Removable Media', - reference: 'https://attack.mitre.org/techniques/T1091', + reference: 'https://attack.mitre.org/techniques/T1091/', tactics: ['lateral-movement', 'initial-access'], value: 'replicationThroughRemovableMedia', }, @@ -1825,7 +1825,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1496', name: 'Resource Hijacking', - reference: 'https://attack.mitre.org/techniques/T1496', + reference: 'https://attack.mitre.org/techniques/T1496/', tactics: ['impact'], value: 'resourceHijacking', }, @@ -1836,7 +1836,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1207', name: 'Rogue Domain Controller', - reference: 'https://attack.mitre.org/techniques/T1207', + reference: 'https://attack.mitre.org/techniques/T1207/', tactics: ['defense-evasion'], value: 'rogueDomainController', }, @@ -1847,7 +1847,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1014', name: 'Rootkit', - reference: 'https://attack.mitre.org/techniques/T1014', + reference: 'https://attack.mitre.org/techniques/T1014/', tactics: ['defense-evasion'], value: 'rootkit', }, @@ -1858,7 +1858,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1053', name: 'Scheduled Task/Job', - reference: 'https://attack.mitre.org/techniques/T1053', + reference: 'https://attack.mitre.org/techniques/T1053/', tactics: ['execution', 'persistence', 'privilege-escalation'], value: 'scheduledTaskJob', }, @@ -1869,7 +1869,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1029', name: 'Scheduled Transfer', - reference: 'https://attack.mitre.org/techniques/T1029', + reference: 'https://attack.mitre.org/techniques/T1029/', tactics: ['exfiltration'], value: 'scheduledTransfer', }, @@ -1880,7 +1880,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1113', name: 'Screen Capture', - reference: 'https://attack.mitre.org/techniques/T1113', + reference: 'https://attack.mitre.org/techniques/T1113/', tactics: ['collection'], value: 'screenCapture', }, @@ -1891,7 +1891,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1597', name: 'Search Closed Sources', - reference: 'https://attack.mitre.org/techniques/T1597', + reference: 'https://attack.mitre.org/techniques/T1597/', tactics: ['reconnaissance'], value: 'searchClosedSources', }, @@ -1902,7 +1902,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1596', name: 'Search Open Technical Databases', - reference: 'https://attack.mitre.org/techniques/T1596', + reference: 'https://attack.mitre.org/techniques/T1596/', tactics: ['reconnaissance'], value: 'searchOpenTechnicalDatabases', }, @@ -1913,7 +1913,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1593', name: 'Search Open Websites/Domains', - reference: 'https://attack.mitre.org/techniques/T1593', + reference: 'https://attack.mitre.org/techniques/T1593/', tactics: ['reconnaissance'], value: 'searchOpenWebsitesDomains', }, @@ -1924,7 +1924,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1594', name: 'Search Victim-Owned Websites', - reference: 'https://attack.mitre.org/techniques/T1594', + reference: 'https://attack.mitre.org/techniques/T1594/', tactics: ['reconnaissance'], value: 'searchVictimOwnedWebsites', }, @@ -1935,7 +1935,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1505', name: 'Server Software Component', - reference: 'https://attack.mitre.org/techniques/T1505', + reference: 'https://attack.mitre.org/techniques/T1505/', tactics: ['persistence'], value: 'serverSoftwareComponent', }, @@ -1946,7 +1946,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1648', name: 'Serverless Execution', - reference: 'https://attack.mitre.org/techniques/T1648', + reference: 'https://attack.mitre.org/techniques/T1648/', tactics: ['execution'], value: 'serverlessExecution', }, @@ -1957,7 +1957,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1489', name: 'Service Stop', - reference: 'https://attack.mitre.org/techniques/T1489', + reference: 'https://attack.mitre.org/techniques/T1489/', tactics: ['impact'], value: 'serviceStop', }, @@ -1968,7 +1968,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1129', name: 'Shared Modules', - reference: 'https://attack.mitre.org/techniques/T1129', + reference: 'https://attack.mitre.org/techniques/T1129/', tactics: ['execution'], value: 'sharedModules', }, @@ -1979,7 +1979,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1072', name: 'Software Deployment Tools', - reference: 'https://attack.mitre.org/techniques/T1072', + reference: 'https://attack.mitre.org/techniques/T1072/', tactics: ['execution', 'lateral-movement'], value: 'softwareDeploymentTools', }, @@ -1990,7 +1990,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1518', name: 'Software Discovery', - reference: 'https://attack.mitre.org/techniques/T1518', + reference: 'https://attack.mitre.org/techniques/T1518/', tactics: ['discovery'], value: 'softwareDiscovery', }, @@ -2001,7 +2001,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1608', name: 'Stage Capabilities', - reference: 'https://attack.mitre.org/techniques/T1608', + reference: 'https://attack.mitre.org/techniques/T1608/', tactics: ['resource-development'], value: 'stageCapabilities', }, @@ -2012,7 +2012,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1528', name: 'Steal Application Access Token', - reference: 'https://attack.mitre.org/techniques/T1528', + reference: 'https://attack.mitre.org/techniques/T1528/', tactics: ['credential-access'], value: 'stealApplicationAccessToken', }, @@ -2023,7 +2023,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1539', name: 'Steal Web Session Cookie', - reference: 'https://attack.mitre.org/techniques/T1539', + reference: 'https://attack.mitre.org/techniques/T1539/', tactics: ['credential-access'], value: 'stealWebSessionCookie', }, @@ -2034,7 +2034,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1649', name: 'Steal or Forge Authentication Certificates', - reference: 'https://attack.mitre.org/techniques/T1649', + reference: 'https://attack.mitre.org/techniques/T1649/', tactics: ['credential-access'], value: 'stealOrForgeAuthenticationCertificates', }, @@ -2045,7 +2045,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1558', name: 'Steal or Forge Kerberos Tickets', - reference: 'https://attack.mitre.org/techniques/T1558', + reference: 'https://attack.mitre.org/techniques/T1558/', tactics: ['credential-access'], value: 'stealOrForgeKerberosTickets', }, @@ -2056,7 +2056,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1553', name: 'Subvert Trust Controls', - reference: 'https://attack.mitre.org/techniques/T1553', + reference: 'https://attack.mitre.org/techniques/T1553/', tactics: ['defense-evasion'], value: 'subvertTrustControls', }, @@ -2067,7 +2067,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1195', name: 'Supply Chain Compromise', - reference: 'https://attack.mitre.org/techniques/T1195', + reference: 'https://attack.mitre.org/techniques/T1195/', tactics: ['initial-access'], value: 'supplyChainCompromise', }, @@ -2078,7 +2078,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1218', name: 'System Binary Proxy Execution', - reference: 'https://attack.mitre.org/techniques/T1218', + reference: 'https://attack.mitre.org/techniques/T1218/', tactics: ['defense-evasion'], value: 'systemBinaryProxyExecution', }, @@ -2089,7 +2089,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1082', name: 'System Information Discovery', - reference: 'https://attack.mitre.org/techniques/T1082', + reference: 'https://attack.mitre.org/techniques/T1082/', tactics: ['discovery'], value: 'systemInformationDiscovery', }, @@ -2100,7 +2100,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1614', name: 'System Location Discovery', - reference: 'https://attack.mitre.org/techniques/T1614', + reference: 'https://attack.mitre.org/techniques/T1614/', tactics: ['discovery'], value: 'systemLocationDiscovery', }, @@ -2111,7 +2111,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1016', name: 'System Network Configuration Discovery', - reference: 'https://attack.mitre.org/techniques/T1016', + reference: 'https://attack.mitre.org/techniques/T1016/', tactics: ['discovery'], value: 'systemNetworkConfigurationDiscovery', }, @@ -2122,7 +2122,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1049', name: 'System Network Connections Discovery', - reference: 'https://attack.mitre.org/techniques/T1049', + reference: 'https://attack.mitre.org/techniques/T1049/', tactics: ['discovery'], value: 'systemNetworkConnectionsDiscovery', }, @@ -2133,7 +2133,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1033', name: 'System Owner/User Discovery', - reference: 'https://attack.mitre.org/techniques/T1033', + reference: 'https://attack.mitre.org/techniques/T1033/', tactics: ['discovery'], value: 'systemOwnerUserDiscovery', }, @@ -2144,7 +2144,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1216', name: 'System Script Proxy Execution', - reference: 'https://attack.mitre.org/techniques/T1216', + reference: 'https://attack.mitre.org/techniques/T1216/', tactics: ['defense-evasion'], value: 'systemScriptProxyExecution', }, @@ -2155,7 +2155,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1007', name: 'System Service Discovery', - reference: 'https://attack.mitre.org/techniques/T1007', + reference: 'https://attack.mitre.org/techniques/T1007/', tactics: ['discovery'], value: 'systemServiceDiscovery', }, @@ -2166,7 +2166,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1569', name: 'System Services', - reference: 'https://attack.mitre.org/techniques/T1569', + reference: 'https://attack.mitre.org/techniques/T1569/', tactics: ['execution'], value: 'systemServices', }, @@ -2177,7 +2177,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1529', name: 'System Shutdown/Reboot', - reference: 'https://attack.mitre.org/techniques/T1529', + reference: 'https://attack.mitre.org/techniques/T1529/', tactics: ['impact'], value: 'systemShutdownReboot', }, @@ -2188,7 +2188,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1124', name: 'System Time Discovery', - reference: 'https://attack.mitre.org/techniques/T1124', + reference: 'https://attack.mitre.org/techniques/T1124/', tactics: ['discovery'], value: 'systemTimeDiscovery', }, @@ -2199,7 +2199,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1080', name: 'Taint Shared Content', - reference: 'https://attack.mitre.org/techniques/T1080', + reference: 'https://attack.mitre.org/techniques/T1080/', tactics: ['lateral-movement'], value: 'taintSharedContent', }, @@ -2210,7 +2210,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1221', name: 'Template Injection', - reference: 'https://attack.mitre.org/techniques/T1221', + reference: 'https://attack.mitre.org/techniques/T1221/', tactics: ['defense-evasion'], value: 'templateInjection', }, @@ -2221,7 +2221,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1205', name: 'Traffic Signaling', - reference: 'https://attack.mitre.org/techniques/T1205', + reference: 'https://attack.mitre.org/techniques/T1205/', tactics: ['defense-evasion', 'persistence', 'command-and-control'], value: 'trafficSignaling', }, @@ -2232,7 +2232,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1537', name: 'Transfer Data to Cloud Account', - reference: 'https://attack.mitre.org/techniques/T1537', + reference: 'https://attack.mitre.org/techniques/T1537/', tactics: ['exfiltration'], value: 'transferDataToCloudAccount', }, @@ -2243,7 +2243,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1127', name: 'Trusted Developer Utilities Proxy Execution', - reference: 'https://attack.mitre.org/techniques/T1127', + reference: 'https://attack.mitre.org/techniques/T1127/', tactics: ['defense-evasion'], value: 'trustedDeveloperUtilitiesProxyExecution', }, @@ -2254,7 +2254,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1199', name: 'Trusted Relationship', - reference: 'https://attack.mitre.org/techniques/T1199', + reference: 'https://attack.mitre.org/techniques/T1199/', tactics: ['initial-access'], value: 'trustedRelationship', }, @@ -2265,7 +2265,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1552', name: 'Unsecured Credentials', - reference: 'https://attack.mitre.org/techniques/T1552', + reference: 'https://attack.mitre.org/techniques/T1552/', tactics: ['credential-access'], value: 'unsecuredCredentials', }, @@ -2276,7 +2276,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1535', name: 'Unused/Unsupported Cloud Regions', - reference: 'https://attack.mitre.org/techniques/T1535', + reference: 'https://attack.mitre.org/techniques/T1535/', tactics: ['defense-evasion'], value: 'unusedUnsupportedCloudRegions', }, @@ -2287,7 +2287,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1550', name: 'Use Alternate Authentication Material', - reference: 'https://attack.mitre.org/techniques/T1550', + reference: 'https://attack.mitre.org/techniques/T1550/', tactics: ['defense-evasion', 'lateral-movement'], value: 'useAlternateAuthenticationMaterial', }, @@ -2298,7 +2298,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1204', name: 'User Execution', - reference: 'https://attack.mitre.org/techniques/T1204', + reference: 'https://attack.mitre.org/techniques/T1204/', tactics: ['execution'], value: 'userExecution', }, @@ -2309,7 +2309,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1078', name: 'Valid Accounts', - reference: 'https://attack.mitre.org/techniques/T1078', + reference: 'https://attack.mitre.org/techniques/T1078/', tactics: ['defense-evasion', 'persistence', 'privilege-escalation', 'initial-access'], value: 'validAccounts', }, @@ -2320,7 +2320,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1125', name: 'Video Capture', - reference: 'https://attack.mitre.org/techniques/T1125', + reference: 'https://attack.mitre.org/techniques/T1125/', tactics: ['collection'], value: 'videoCapture', }, @@ -2331,7 +2331,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1497', name: 'Virtualization/Sandbox Evasion', - reference: 'https://attack.mitre.org/techniques/T1497', + reference: 'https://attack.mitre.org/techniques/T1497/', tactics: ['defense-evasion', 'discovery'], value: 'virtualizationSandboxEvasion', }, @@ -2342,7 +2342,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1600', name: 'Weaken Encryption', - reference: 'https://attack.mitre.org/techniques/T1600', + reference: 'https://attack.mitre.org/techniques/T1600/', tactics: ['defense-evasion'], value: 'weakenEncryption', }, @@ -2353,7 +2353,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1102', name: 'Web Service', - reference: 'https://attack.mitre.org/techniques/T1102', + reference: 'https://attack.mitre.org/techniques/T1102/', tactics: ['command-and-control'], value: 'webService', }, @@ -2364,7 +2364,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1047', name: 'Windows Management Instrumentation', - reference: 'https://attack.mitre.org/techniques/T1047', + reference: 'https://attack.mitre.org/techniques/T1047/', tactics: ['execution'], value: 'windowsManagementInstrumentation', }, @@ -2375,7 +2375,7 @@ export const techniques: MitreTechnique[] = [ ), id: 'T1220', name: 'XSL Script Processing', - reference: 'https://attack.mitre.org/techniques/T1220', + reference: 'https://attack.mitre.org/techniques/T1220/', tactics: ['defense-evasion'], value: 'xslScriptProcessing', }, @@ -2389,7 +2389,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.008', name: '/etc/passwd and /etc/shadow', - reference: 'https://attack.mitre.org/techniques/T1003/008', + reference: 'https://attack.mitre.org/techniques/T1003/008/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'etcPasswdAndEtcShadow', @@ -2401,7 +2401,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1557.002', name: 'ARP Cache Poisoning', - reference: 'https://attack.mitre.org/techniques/T1557/002', + reference: 'https://attack.mitre.org/techniques/T1557/002/', tactics: ['credential-access', 'collection'], techniqueId: 'T1557', value: 'arpCachePoisoning', @@ -2413,7 +2413,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1558.004', name: 'AS-REP Roasting', - reference: 'https://attack.mitre.org/techniques/T1558/004', + reference: 'https://attack.mitre.org/techniques/T1558/004/', tactics: ['credential-access'], techniqueId: 'T1558', value: 'asRepRoasting', @@ -2425,7 +2425,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.008', name: 'Accessibility Features', - reference: 'https://attack.mitre.org/techniques/T1546/008', + reference: 'https://attack.mitre.org/techniques/T1546/008/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'accessibilityFeatures', @@ -2437,7 +2437,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.014', name: 'Active Setup', - reference: 'https://attack.mitre.org/techniques/T1547/014', + reference: 'https://attack.mitre.org/techniques/T1547/014/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'activeSetup', @@ -2449,7 +2449,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1137.006', name: 'Add-ins', - reference: 'https://attack.mitre.org/techniques/T1137/006', + reference: 'https://attack.mitre.org/techniques/T1137/006/', tactics: ['persistence'], techniqueId: 'T1137', value: 'addIns', @@ -2461,7 +2461,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1098.001', name: 'Additional Cloud Credentials', - reference: 'https://attack.mitre.org/techniques/T1098/001', + reference: 'https://attack.mitre.org/techniques/T1098/001/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1098', value: 'additionalCloudCredentials', @@ -2473,7 +2473,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1098.003', name: 'Additional Cloud Roles', - reference: 'https://attack.mitre.org/techniques/T1098/003', + reference: 'https://attack.mitre.org/techniques/T1098/003/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1098', value: 'additionalCloudRoles', @@ -2485,7 +2485,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1098.006', name: 'Additional Container Cluster Roles', - reference: 'https://attack.mitre.org/techniques/T1098/006', + reference: 'https://attack.mitre.org/techniques/T1098/006/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1098', value: 'additionalContainerClusterRoles', @@ -2497,7 +2497,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1098.002', name: 'Additional Email Delegate Permissions', - reference: 'https://attack.mitre.org/techniques/T1098/002', + reference: 'https://attack.mitre.org/techniques/T1098/002/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1098', value: 'additionalEmailDelegatePermissions', @@ -2509,7 +2509,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.009', name: 'AppCert DLLs', - reference: 'https://attack.mitre.org/techniques/T1546/009', + reference: 'https://attack.mitre.org/techniques/T1546/009/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'appCertDlLs', @@ -2521,7 +2521,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.014', name: 'AppDomainManager', - reference: 'https://attack.mitre.org/techniques/T1574/014', + reference: 'https://attack.mitre.org/techniques/T1574/014/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'appDomainManager', @@ -2533,7 +2533,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.010', name: 'AppInit DLLs', - reference: 'https://attack.mitre.org/techniques/T1546/010', + reference: 'https://attack.mitre.org/techniques/T1546/010/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'appInitDlLs', @@ -2545,7 +2545,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.002', name: 'AppleScript', - reference: 'https://attack.mitre.org/techniques/T1059/002', + reference: 'https://attack.mitre.org/techniques/T1059/002/', tactics: ['execution'], techniqueId: 'T1059', value: 'appleScript', @@ -2557,7 +2557,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1550.001', name: 'Application Access Token', - reference: 'https://attack.mitre.org/techniques/T1550/001', + reference: 'https://attack.mitre.org/techniques/T1550/001/', tactics: ['defense-evasion', 'lateral-movement'], techniqueId: 'T1550', value: 'applicationAccessToken', @@ -2569,7 +2569,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1499.003', name: 'Application Exhaustion Flood', - reference: 'https://attack.mitre.org/techniques/T1499/003', + reference: 'https://attack.mitre.org/techniques/T1499/003/', tactics: ['impact'], techniqueId: 'T1499', value: 'applicationExhaustionFlood', @@ -2581,7 +2581,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.011', name: 'Application Shimming', - reference: 'https://attack.mitre.org/techniques/T1546/011', + reference: 'https://attack.mitre.org/techniques/T1546/011/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'applicationShimming', @@ -2593,7 +2593,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1499.004', name: 'Application or System Exploitation', - reference: 'https://attack.mitre.org/techniques/T1499/004', + reference: 'https://attack.mitre.org/techniques/T1499/004/', tactics: ['impact'], techniqueId: 'T1499', value: 'applicationOrSystemExploitation', @@ -2605,7 +2605,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1560.003', name: 'Archive via Custom Method', - reference: 'https://attack.mitre.org/techniques/T1560/003', + reference: 'https://attack.mitre.org/techniques/T1560/003/', tactics: ['collection'], techniqueId: 'T1560', value: 'archiveViaCustomMethod', @@ -2617,7 +2617,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1560.002', name: 'Archive via Library', - reference: 'https://attack.mitre.org/techniques/T1560/002', + reference: 'https://attack.mitre.org/techniques/T1560/002/', tactics: ['collection'], techniqueId: 'T1560', value: 'archiveViaLibrary', @@ -2629,7 +2629,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1560.001', name: 'Archive via Utility', - reference: 'https://attack.mitre.org/techniques/T1560/001', + reference: 'https://attack.mitre.org/techniques/T1560/001/', tactics: ['collection'], techniqueId: 'T1560', value: 'archiveViaUtility', @@ -2641,7 +2641,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.007', name: 'Artificial Intelligence', - reference: 'https://attack.mitre.org/techniques/T1588/007', + reference: 'https://attack.mitre.org/techniques/T1588/007/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'artificialIntelligence', @@ -2653,7 +2653,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1573.002', name: 'Asymmetric Cryptography', - reference: 'https://attack.mitre.org/techniques/T1573/002', + reference: 'https://attack.mitre.org/techniques/T1573/002/', tactics: ['command-and-control'], techniqueId: 'T1573', value: 'asymmetricCryptography', @@ -2665,7 +2665,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.004', name: 'Asynchronous Procedure Call', - reference: 'https://attack.mitre.org/techniques/T1055/004', + reference: 'https://attack.mitre.org/techniques/T1055/004/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'asynchronousProcedureCall', @@ -2677,7 +2677,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1053.002', name: 'At', - reference: 'https://attack.mitre.org/techniques/T1053/002', + reference: 'https://attack.mitre.org/techniques/T1053/002/', tactics: ['execution', 'persistence', 'privilege-escalation'], techniqueId: 'T1053', value: 'at', @@ -2689,7 +2689,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.002', name: 'Authentication Package', - reference: 'https://attack.mitre.org/techniques/T1547/002', + reference: 'https://attack.mitre.org/techniques/T1547/002/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'authenticationPackage', @@ -2701,7 +2701,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.010', name: 'AutoHotKey & AutoIT', - reference: 'https://attack.mitre.org/techniques/T1059/010', + reference: 'https://attack.mitre.org/techniques/T1059/010/', tactics: ['execution'], techniqueId: 'T1059', value: 'autoHotKeyAutoIt', @@ -2713,7 +2713,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.003', name: 'Bash History', - reference: 'https://attack.mitre.org/techniques/T1552/003', + reference: 'https://attack.mitre.org/techniques/T1552/003/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'bashHistory', @@ -2725,7 +2725,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1102.002', name: 'Bidirectional Communication', - reference: 'https://attack.mitre.org/techniques/T1102/002', + reference: 'https://attack.mitre.org/techniques/T1102/002/', tactics: ['command-and-control'], techniqueId: 'T1102', value: 'bidirectionalCommunication', @@ -2737,7 +2737,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.001', name: 'Binary Padding', - reference: 'https://attack.mitre.org/techniques/T1027/001', + reference: 'https://attack.mitre.org/techniques/T1027/001/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'binaryPadding', @@ -2749,7 +2749,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1542.003', name: 'Bootkit', - reference: 'https://attack.mitre.org/techniques/T1542/003', + reference: 'https://attack.mitre.org/techniques/T1542/003/', tactics: ['persistence', 'defense-evasion'], techniqueId: 'T1542', value: 'bootkit', @@ -2761,7 +2761,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.005', name: 'Botnet', - reference: 'https://attack.mitre.org/techniques/T1583/005', + reference: 'https://attack.mitre.org/techniques/T1583/005/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'botnet', @@ -2773,7 +2773,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.005', name: 'Botnet', - reference: 'https://attack.mitre.org/techniques/T1584/005', + reference: 'https://attack.mitre.org/techniques/T1584/005/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'botnet', @@ -2785,7 +2785,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.009', name: 'Break Process Trees', - reference: 'https://attack.mitre.org/techniques/T1036/009', + reference: 'https://attack.mitre.org/techniques/T1036/009/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'breakProcessTrees', @@ -2797,7 +2797,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1591.002', name: 'Business Relationships', - reference: 'https://attack.mitre.org/techniques/T1591/002', + reference: 'https://attack.mitre.org/techniques/T1591/002/', tactics: ['reconnaissance'], techniqueId: 'T1591', value: 'businessRelationships', @@ -2809,7 +2809,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1548.002', name: 'Bypass User Account Control', - reference: 'https://attack.mitre.org/techniques/T1548/002', + reference: 'https://attack.mitre.org/techniques/T1548/002/', tactics: ['privilege-escalation', 'defense-evasion'], techniqueId: 'T1548', value: 'bypassUserAccountControl', @@ -2821,7 +2821,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1596.004', name: 'CDNs', - reference: 'https://attack.mitre.org/techniques/T1596/004', + reference: 'https://attack.mitre.org/techniques/T1596/004/', tactics: ['reconnaissance'], techniqueId: 'T1596', value: 'cdNs', @@ -2833,7 +2833,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.003', name: 'CMSTP', - reference: 'https://attack.mitre.org/techniques/T1218/003', + reference: 'https://attack.mitre.org/techniques/T1218/003/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'cmstp', @@ -2845,7 +2845,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.012', name: 'COR_PROFILER', - reference: 'https://attack.mitre.org/techniques/T1574/012', + reference: 'https://attack.mitre.org/techniques/T1574/012/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'corProfiler', @@ -2857,7 +2857,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.005', name: 'Cached Domain Credentials', - reference: 'https://attack.mitre.org/techniques/T1003/005', + reference: 'https://attack.mitre.org/techniques/T1003/005/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'cachedDomainCredentials', @@ -2869,7 +2869,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.001', name: 'Change Default File Association', - reference: 'https://attack.mitre.org/techniques/T1546/001', + reference: 'https://attack.mitre.org/techniques/T1546/001/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'changeDefaultFileAssociation', @@ -2881,7 +2881,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.008', name: 'Chat Messages', - reference: 'https://attack.mitre.org/techniques/T1552/008', + reference: 'https://attack.mitre.org/techniques/T1552/008/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'chatMessages', @@ -2893,7 +2893,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.003', name: 'Clear Command History', - reference: 'https://attack.mitre.org/techniques/T1070/003', + reference: 'https://attack.mitre.org/techniques/T1070/003/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'clearCommandHistory', @@ -2905,7 +2905,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.002', name: 'Clear Linux or Mac System Logs', - reference: 'https://attack.mitre.org/techniques/T1070/002', + reference: 'https://attack.mitre.org/techniques/T1070/002/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'clearLinuxOrMacSystemLogs', @@ -2917,7 +2917,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.008', name: 'Clear Mailbox Data', - reference: 'https://attack.mitre.org/techniques/T1070/008', + reference: 'https://attack.mitre.org/techniques/T1070/008/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'clearMailboxData', @@ -2929,7 +2929,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.007', name: 'Clear Network Connection History and Configurations', - reference: 'https://attack.mitre.org/techniques/T1070/007', + reference: 'https://attack.mitre.org/techniques/T1070/007/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'clearNetworkConnectionHistoryAndConfigurations', @@ -2941,7 +2941,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.009', name: 'Clear Persistence', - reference: 'https://attack.mitre.org/techniques/T1070/009', + reference: 'https://attack.mitre.org/techniques/T1070/009/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'clearPersistence', @@ -2953,7 +2953,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.001', name: 'Clear Windows Event Logs', - reference: 'https://attack.mitre.org/techniques/T1070/001', + reference: 'https://attack.mitre.org/techniques/T1070/001/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'clearWindowsEventLogs', @@ -2965,7 +2965,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1592.004', name: 'Client Configurations', - reference: 'https://attack.mitre.org/techniques/T1592/004', + reference: 'https://attack.mitre.org/techniques/T1592/004/', tactics: ['reconnaissance'], techniqueId: 'T1592', value: 'clientConfigurations', @@ -2977,7 +2977,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.009', name: 'Cloud API', - reference: 'https://attack.mitre.org/techniques/T1059/009', + reference: 'https://attack.mitre.org/techniques/T1059/009/', tactics: ['execution'], techniqueId: 'T1059', value: 'cloudApi', @@ -2989,7 +2989,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1087.004', name: 'Cloud Account', - reference: 'https://attack.mitre.org/techniques/T1087/004', + reference: 'https://attack.mitre.org/techniques/T1087/004/', tactics: ['discovery'], techniqueId: 'T1087', value: 'cloudAccount', @@ -3001,7 +3001,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1136.003', name: 'Cloud Account', - reference: 'https://attack.mitre.org/techniques/T1136/003', + reference: 'https://attack.mitre.org/techniques/T1136/003/', tactics: ['persistence'], techniqueId: 'T1136', value: 'cloudAccount', @@ -3013,7 +3013,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1586.003', name: 'Cloud Accounts', - reference: 'https://attack.mitre.org/techniques/T1586/003', + reference: 'https://attack.mitre.org/techniques/T1586/003/', tactics: ['resource-development'], techniqueId: 'T1586', value: 'cloudAccounts', @@ -3025,7 +3025,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1585.003', name: 'Cloud Accounts', - reference: 'https://attack.mitre.org/techniques/T1585/003', + reference: 'https://attack.mitre.org/techniques/T1585/003/', tactics: ['resource-development'], techniqueId: 'T1585', value: 'cloudAccounts', @@ -3037,7 +3037,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1078.004', name: 'Cloud Accounts', - reference: 'https://attack.mitre.org/techniques/T1078/004', + reference: 'https://attack.mitre.org/techniques/T1078/004/', tactics: ['defense-evasion', 'persistence', 'privilege-escalation', 'initial-access'], techniqueId: 'T1078', value: 'cloudAccounts', @@ -3049,7 +3049,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1069.003', name: 'Cloud Groups', - reference: 'https://attack.mitre.org/techniques/T1069/003', + reference: 'https://attack.mitre.org/techniques/T1069/003/', tactics: ['discovery'], techniqueId: 'T1069', value: 'cloudGroups', @@ -3061,7 +3061,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.005', name: 'Cloud Instance Metadata API', - reference: 'https://attack.mitre.org/techniques/T1552/005', + reference: 'https://attack.mitre.org/techniques/T1552/005/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'cloudInstanceMetadataApi', @@ -3073,7 +3073,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1555.006', name: 'Cloud Secrets Management Stores', - reference: 'https://attack.mitre.org/techniques/T1555/006', + reference: 'https://attack.mitre.org/techniques/T1555/006/', tactics: ['credential-access'], techniqueId: 'T1555', value: 'cloudSecretsManagementStores', @@ -3085,7 +3085,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.007', name: 'Cloud Services', - reference: 'https://attack.mitre.org/techniques/T1021/007', + reference: 'https://attack.mitre.org/techniques/T1021/007/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'cloudServices', @@ -3097,7 +3097,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1593.003', name: 'Code Repositories', - reference: 'https://attack.mitre.org/techniques/T1593/003', + reference: 'https://attack.mitre.org/techniques/T1593/003/', tactics: ['reconnaissance'], techniqueId: 'T1593', value: 'codeRepositories', @@ -3109,7 +3109,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1213.003', name: 'Code Repositories', - reference: 'https://attack.mitre.org/techniques/T1213/003', + reference: 'https://attack.mitre.org/techniques/T1213/003/', tactics: ['collection'], techniqueId: 'T1213', value: 'codeRepositories', @@ -3121,7 +3121,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1553.002', name: 'Code Signing', - reference: 'https://attack.mitre.org/techniques/T1553/002', + reference: 'https://attack.mitre.org/techniques/T1553/002/', tactics: ['defense-evasion'], techniqueId: 'T1553', value: 'codeSigning', @@ -3133,7 +3133,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1587.002', name: 'Code Signing Certificates', - reference: 'https://attack.mitre.org/techniques/T1587/002', + reference: 'https://attack.mitre.org/techniques/T1587/002/', tactics: ['resource-development'], techniqueId: 'T1587', value: 'codeSigningCertificates', @@ -3145,7 +3145,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.003', name: 'Code Signing Certificates', - reference: 'https://attack.mitre.org/techniques/T1588/003', + reference: 'https://attack.mitre.org/techniques/T1588/003/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'codeSigningCertificates', @@ -3157,7 +3157,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1553.006', name: 'Code Signing Policy Modification', - reference: 'https://attack.mitre.org/techniques/T1553/006', + reference: 'https://attack.mitre.org/techniques/T1553/006/', tactics: ['defense-evasion'], techniqueId: 'T1553', value: 'codeSigningPolicyModification', @@ -3169,7 +3169,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.010', name: 'Command Obfuscation', - reference: 'https://attack.mitre.org/techniques/T1027/010', + reference: 'https://attack.mitre.org/techniques/T1027/010/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'commandObfuscation', @@ -3181,7 +3181,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.004', name: 'Compile After Delivery', - reference: 'https://attack.mitre.org/techniques/T1027/004', + reference: 'https://attack.mitre.org/techniques/T1027/004/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'compileAfterDelivery', @@ -3193,7 +3193,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.001', name: 'Compiled HTML File', - reference: 'https://attack.mitre.org/techniques/T1218/001', + reference: 'https://attack.mitre.org/techniques/T1218/001/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'compiledHtmlFile', @@ -3205,7 +3205,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1542.002', name: 'Component Firmware', - reference: 'https://attack.mitre.org/techniques/T1542/002', + reference: 'https://attack.mitre.org/techniques/T1542/002/', tactics: ['persistence', 'defense-evasion'], techniqueId: 'T1542', value: 'componentFirmware', @@ -3217,7 +3217,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1559.001', name: 'Component Object Model', - reference: 'https://attack.mitre.org/techniques/T1559/001', + reference: 'https://attack.mitre.org/techniques/T1559/001/', tactics: ['execution'], techniqueId: 'T1559', value: 'componentObjectModel', @@ -3229,7 +3229,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.015', name: 'Component Object Model Hijacking', - reference: 'https://attack.mitre.org/techniques/T1546/015', + reference: 'https://attack.mitre.org/techniques/T1546/015/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'componentObjectModelHijacking', @@ -3241,7 +3241,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1195.003', name: 'Compromise Hardware Supply Chain', - reference: 'https://attack.mitre.org/techniques/T1195/003', + reference: 'https://attack.mitre.org/techniques/T1195/003/', tactics: ['initial-access'], techniqueId: 'T1195', value: 'compromiseHardwareSupplyChain', @@ -3253,7 +3253,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1195.001', name: 'Compromise Software Dependencies and Development Tools', - reference: 'https://attack.mitre.org/techniques/T1195/001', + reference: 'https://attack.mitre.org/techniques/T1195/001/', tactics: ['initial-access'], techniqueId: 'T1195', value: 'compromiseSoftwareDependenciesAndDevelopmentTools', @@ -3265,7 +3265,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1195.002', name: 'Compromise Software Supply Chain', - reference: 'https://attack.mitre.org/techniques/T1195/002', + reference: 'https://attack.mitre.org/techniques/T1195/002/', tactics: ['initial-access'], techniqueId: 'T1195', value: 'compromiseSoftwareSupplyChain', @@ -3277,7 +3277,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.009', name: 'Conditional Access Policies', - reference: 'https://attack.mitre.org/techniques/T1556/009', + reference: 'https://attack.mitre.org/techniques/T1556/009/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'conditionalAccessPolicies', @@ -3289,7 +3289,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1213.001', name: 'Confluence', - reference: 'https://attack.mitre.org/techniques/T1213/001', + reference: 'https://attack.mitre.org/techniques/T1213/001/', tactics: ['collection'], techniqueId: 'T1213', value: 'confluence', @@ -3301,7 +3301,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.007', name: 'Container API', - reference: 'https://attack.mitre.org/techniques/T1552/007', + reference: 'https://attack.mitre.org/techniques/T1552/007/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'containerApi', @@ -3313,7 +3313,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1053.007', name: 'Container Orchestration Job', - reference: 'https://attack.mitre.org/techniques/T1053/007', + reference: 'https://attack.mitre.org/techniques/T1053/007/', tactics: ['execution', 'persistence', 'privilege-escalation'], techniqueId: 'T1053', value: 'containerOrchestrationJob', @@ -3325,7 +3325,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1543.005', name: 'Container Service', - reference: 'https://attack.mitre.org/techniques/T1543/005', + reference: 'https://attack.mitre.org/techniques/T1543/005/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1543', value: 'containerService', @@ -3337,7 +3337,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.002', name: 'Control Panel', - reference: 'https://attack.mitre.org/techniques/T1218/002', + reference: 'https://attack.mitre.org/techniques/T1218/002/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'controlPanel', @@ -3349,7 +3349,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1578.002', name: 'Create Cloud Instance', - reference: 'https://attack.mitre.org/techniques/T1578/002', + reference: 'https://attack.mitre.org/techniques/T1578/002/', tactics: ['defense-evasion'], techniqueId: 'T1578', value: 'createCloudInstance', @@ -3361,7 +3361,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1134.002', name: 'Create Process with Token', - reference: 'https://attack.mitre.org/techniques/T1134/002', + reference: 'https://attack.mitre.org/techniques/T1134/002/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1134', value: 'createProcessWithToken', @@ -3373,7 +3373,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1578.001', name: 'Create Snapshot', - reference: 'https://attack.mitre.org/techniques/T1578/001', + reference: 'https://attack.mitre.org/techniques/T1578/001/', tactics: ['defense-evasion'], techniqueId: 'T1578', value: 'createSnapshot', @@ -3385,7 +3385,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1056.004', name: 'Credential API Hooking', - reference: 'https://attack.mitre.org/techniques/T1056/004', + reference: 'https://attack.mitre.org/techniques/T1056/004/', tactics: ['collection', 'credential-access'], techniqueId: 'T1056', value: 'credentialApiHooking', @@ -3397,7 +3397,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1110.004', name: 'Credential Stuffing', - reference: 'https://attack.mitre.org/techniques/T1110/004', + reference: 'https://attack.mitre.org/techniques/T1110/004/', tactics: ['credential-access'], techniqueId: 'T1110', value: 'credentialStuffing', @@ -3409,7 +3409,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1589.001', name: 'Credentials', - reference: 'https://attack.mitre.org/techniques/T1589/001', + reference: 'https://attack.mitre.org/techniques/T1589/001/', tactics: ['reconnaissance'], techniqueId: 'T1589', value: 'credentials', @@ -3421,7 +3421,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.001', name: 'Credentials In Files', - reference: 'https://attack.mitre.org/techniques/T1552/001', + reference: 'https://attack.mitre.org/techniques/T1552/001/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'credentialsInFiles', @@ -3433,7 +3433,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1555.003', name: 'Credentials from Web Browsers', - reference: 'https://attack.mitre.org/techniques/T1555/003', + reference: 'https://attack.mitre.org/techniques/T1555/003/', tactics: ['credential-access'], techniqueId: 'T1555', value: 'credentialsFromWebBrowsers', @@ -3445,7 +3445,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.002', name: 'Credentials in Registry', - reference: 'https://attack.mitre.org/techniques/T1552/002', + reference: 'https://attack.mitre.org/techniques/T1552/002/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'credentialsInRegistry', @@ -3457,7 +3457,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1053.003', name: 'Cron', - reference: 'https://attack.mitre.org/techniques/T1053/003', + reference: 'https://attack.mitre.org/techniques/T1053/003/', tactics: ['execution', 'persistence', 'privilege-escalation'], techniqueId: 'T1053', value: 'cron', @@ -3469,7 +3469,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.006', name: 'DCSync', - reference: 'https://attack.mitre.org/techniques/T1003/006', + reference: 'https://attack.mitre.org/techniques/T1003/006/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'dcSync', @@ -3481,7 +3481,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1557.003', name: 'DHCP Spoofing', - reference: 'https://attack.mitre.org/techniques/T1557/003', + reference: 'https://attack.mitre.org/techniques/T1557/003/', tactics: ['credential-access', 'collection'], techniqueId: 'T1557', value: 'dhcpSpoofing', @@ -3493,7 +3493,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.001', name: 'DLL Search Order Hijacking', - reference: 'https://attack.mitre.org/techniques/T1574/001', + reference: 'https://attack.mitre.org/techniques/T1574/001/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'dllSearchOrderHijacking', @@ -3505,7 +3505,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.002', name: 'DLL Side-Loading', - reference: 'https://attack.mitre.org/techniques/T1574/002', + reference: 'https://attack.mitre.org/techniques/T1574/002/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'dllSideLoading', @@ -3517,7 +3517,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1590.002', name: 'DNS', - reference: 'https://attack.mitre.org/techniques/T1590/002', + reference: 'https://attack.mitre.org/techniques/T1590/002/', tactics: ['reconnaissance'], techniqueId: 'T1590', value: 'dns', @@ -3529,7 +3529,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1071.004', name: 'DNS', - reference: 'https://attack.mitre.org/techniques/T1071/004', + reference: 'https://attack.mitre.org/techniques/T1071/004/', tactics: ['command-and-control'], techniqueId: 'T1071', value: 'dns', @@ -3541,7 +3541,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1568.003', name: 'DNS Calculation', - reference: 'https://attack.mitre.org/techniques/T1568/003', + reference: 'https://attack.mitre.org/techniques/T1568/003/', tactics: ['command-and-control'], techniqueId: 'T1568', value: 'dnsCalculation', @@ -3553,7 +3553,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.002', name: 'DNS Server', - reference: 'https://attack.mitre.org/techniques/T1583/002', + reference: 'https://attack.mitre.org/techniques/T1583/002/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'dnsServer', @@ -3565,7 +3565,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.002', name: 'DNS Server', - reference: 'https://attack.mitre.org/techniques/T1584/002', + reference: 'https://attack.mitre.org/techniques/T1584/002/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'dnsServer', @@ -3577,7 +3577,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1596.001', name: 'DNS/Passive DNS', - reference: 'https://attack.mitre.org/techniques/T1596/001', + reference: 'https://attack.mitre.org/techniques/T1596/001/', tactics: ['reconnaissance'], techniqueId: 'T1596', value: 'dnsPassiveDns', @@ -3589,7 +3589,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1102.001', name: 'Dead Drop Resolver', - reference: 'https://attack.mitre.org/techniques/T1102/001', + reference: 'https://attack.mitre.org/techniques/T1102/001/', tactics: ['command-and-control'], techniqueId: 'T1102', value: 'deadDropResolver', @@ -3601,7 +3601,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1078.001', name: 'Default Accounts', - reference: 'https://attack.mitre.org/techniques/T1078/001', + reference: 'https://attack.mitre.org/techniques/T1078/001/', tactics: ['defense-evasion', 'persistence', 'privilege-escalation', 'initial-access'], techniqueId: 'T1078', value: 'defaultAccounts', @@ -3613,7 +3613,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1578.003', name: 'Delete Cloud Instance', - reference: 'https://attack.mitre.org/techniques/T1578/003', + reference: 'https://attack.mitre.org/techniques/T1578/003/', tactics: ['defense-evasion'], techniqueId: 'T1578', value: 'deleteCloudInstance', @@ -3625,7 +3625,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1591.001', name: 'Determine Physical Locations', - reference: 'https://attack.mitre.org/techniques/T1591/001', + reference: 'https://attack.mitre.org/techniques/T1591/001/', tactics: ['reconnaissance'], techniqueId: 'T1591', value: 'determinePhysicalLocations', @@ -3637,7 +3637,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1098.005', name: 'Device Registration', - reference: 'https://attack.mitre.org/techniques/T1098/005', + reference: 'https://attack.mitre.org/techniques/T1098/005/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1098', value: 'deviceRegistration', @@ -3649,7 +3649,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1596.003', name: 'Digital Certificates', - reference: 'https://attack.mitre.org/techniques/T1596/003', + reference: 'https://attack.mitre.org/techniques/T1596/003/', tactics: ['reconnaissance'], techniqueId: 'T1596', value: 'digitalCertificates', @@ -3661,7 +3661,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.004', name: 'Digital Certificates', - reference: 'https://attack.mitre.org/techniques/T1588/004', + reference: 'https://attack.mitre.org/techniques/T1588/004/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'digitalCertificates', @@ -3673,7 +3673,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1587.003', name: 'Digital Certificates', - reference: 'https://attack.mitre.org/techniques/T1587/003', + reference: 'https://attack.mitre.org/techniques/T1587/003/', tactics: ['resource-development'], techniqueId: 'T1587', value: 'digitalCertificates', @@ -3685,7 +3685,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.008', name: 'Direct Cloud VM Connections', - reference: 'https://attack.mitre.org/techniques/T1021/008', + reference: 'https://attack.mitre.org/techniques/T1021/008/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'directCloudVmConnections', @@ -3697,7 +3697,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1498.001', name: 'Direct Network Flood', - reference: 'https://attack.mitre.org/techniques/T1498/001', + reference: 'https://attack.mitre.org/techniques/T1498/001/', tactics: ['impact'], techniqueId: 'T1498', value: 'directNetworkFlood', @@ -3709,7 +3709,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1600.002', name: 'Disable Crypto Hardware', - reference: 'https://attack.mitre.org/techniques/T1600/002', + reference: 'https://attack.mitre.org/techniques/T1600/002/', tactics: ['defense-evasion'], techniqueId: 'T1600', value: 'disableCryptoHardware', @@ -3721,7 +3721,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.002', name: 'Disable Windows Event Logging', - reference: 'https://attack.mitre.org/techniques/T1562/002', + reference: 'https://attack.mitre.org/techniques/T1562/002/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'disableWindowsEventLogging', @@ -3733,7 +3733,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.007', name: 'Disable or Modify Cloud Firewall', - reference: 'https://attack.mitre.org/techniques/T1562/007', + reference: 'https://attack.mitre.org/techniques/T1562/007/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'disableOrModifyCloudFirewall', @@ -3745,7 +3745,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.008', name: 'Disable or Modify Cloud Logs', - reference: 'https://attack.mitre.org/techniques/T1562/008', + reference: 'https://attack.mitre.org/techniques/T1562/008/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'disableOrModifyCloudLogs', @@ -3757,7 +3757,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.012', name: 'Disable or Modify Linux Audit System', - reference: 'https://attack.mitre.org/techniques/T1562/012', + reference: 'https://attack.mitre.org/techniques/T1562/012/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'disableOrModifyLinuxAuditSystem', @@ -3769,7 +3769,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.004', name: 'Disable or Modify System Firewall', - reference: 'https://attack.mitre.org/techniques/T1562/004', + reference: 'https://attack.mitre.org/techniques/T1562/004/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'disableOrModifySystemFirewall', @@ -3781,7 +3781,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.001', name: 'Disable or Modify Tools', - reference: 'https://attack.mitre.org/techniques/T1562/001', + reference: 'https://attack.mitre.org/techniques/T1562/001/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'disableOrModifyTools', @@ -3793,7 +3793,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1561.001', name: 'Disk Content Wipe', - reference: 'https://attack.mitre.org/techniques/T1561/001', + reference: 'https://attack.mitre.org/techniques/T1561/001/', tactics: ['impact'], techniqueId: 'T1561', value: 'diskContentWipe', @@ -3805,7 +3805,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1561.002', name: 'Disk Structure Wipe', - reference: 'https://attack.mitre.org/techniques/T1561/002', + reference: 'https://attack.mitre.org/techniques/T1561/002/', tactics: ['impact'], techniqueId: 'T1561', value: 'diskStructureWipe', @@ -3817,7 +3817,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.003', name: 'Distributed Component Object Model', - reference: 'https://attack.mitre.org/techniques/T1021/003', + reference: 'https://attack.mitre.org/techniques/T1021/003/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'distributedComponentObjectModel', @@ -3829,7 +3829,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1087.002', name: 'Domain Account', - reference: 'https://attack.mitre.org/techniques/T1087/002', + reference: 'https://attack.mitre.org/techniques/T1087/002/', tactics: ['discovery'], techniqueId: 'T1087', value: 'domainAccount', @@ -3841,7 +3841,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1136.002', name: 'Domain Account', - reference: 'https://attack.mitre.org/techniques/T1136/002', + reference: 'https://attack.mitre.org/techniques/T1136/002/', tactics: ['persistence'], techniqueId: 'T1136', value: 'domainAccount', @@ -3853,7 +3853,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1078.002', name: 'Domain Accounts', - reference: 'https://attack.mitre.org/techniques/T1078/002', + reference: 'https://attack.mitre.org/techniques/T1078/002/', tactics: ['defense-evasion', 'persistence', 'privilege-escalation', 'initial-access'], techniqueId: 'T1078', value: 'domainAccounts', @@ -3865,7 +3865,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.001', name: 'Domain Controller Authentication', - reference: 'https://attack.mitre.org/techniques/T1556/001', + reference: 'https://attack.mitre.org/techniques/T1556/001/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'domainControllerAuthentication', @@ -3877,7 +3877,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1090.004', name: 'Domain Fronting', - reference: 'https://attack.mitre.org/techniques/T1090/004', + reference: 'https://attack.mitre.org/techniques/T1090/004/', tactics: ['command-and-control'], techniqueId: 'T1090', value: 'domainFronting', @@ -3889,7 +3889,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1568.002', name: 'Domain Generation Algorithms', - reference: 'https://attack.mitre.org/techniques/T1568/002', + reference: 'https://attack.mitre.org/techniques/T1568/002/', tactics: ['command-and-control'], techniqueId: 'T1568', value: 'domainGenerationAlgorithms', @@ -3901,7 +3901,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1069.002', name: 'Domain Groups', - reference: 'https://attack.mitre.org/techniques/T1069/002', + reference: 'https://attack.mitre.org/techniques/T1069/002/', tactics: ['discovery'], techniqueId: 'T1069', value: 'domainGroups', @@ -3913,7 +3913,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1590.001', name: 'Domain Properties', - reference: 'https://attack.mitre.org/techniques/T1590/001', + reference: 'https://attack.mitre.org/techniques/T1590/001/', tactics: ['reconnaissance'], techniqueId: 'T1590', value: 'domainProperties', @@ -3925,7 +3925,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.001', name: 'Domains', - reference: 'https://attack.mitre.org/techniques/T1583/001', + reference: 'https://attack.mitre.org/techniques/T1583/001/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'domains', @@ -3937,7 +3937,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.001', name: 'Domains', - reference: 'https://attack.mitre.org/techniques/T1584/001', + reference: 'https://attack.mitre.org/techniques/T1584/001/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'domains', @@ -3949,7 +3949,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.007', name: 'Double File Extension', - reference: 'https://attack.mitre.org/techniques/T1036/007', + reference: 'https://attack.mitre.org/techniques/T1036/007/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'doubleFileExtension', @@ -3961,7 +3961,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.010', name: 'Downgrade Attack', - reference: 'https://attack.mitre.org/techniques/T1562/010', + reference: 'https://attack.mitre.org/techniques/T1562/010/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'downgradeAttack', @@ -3973,7 +3973,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1601.002', name: 'Downgrade System Image', - reference: 'https://attack.mitre.org/techniques/T1601/002', + reference: 'https://attack.mitre.org/techniques/T1601/002/', tactics: ['defense-evasion'], techniqueId: 'T1601', value: 'downgradeSystemImage', @@ -3985,7 +3985,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1608.004', name: 'Drive-by Target', - reference: 'https://attack.mitre.org/techniques/T1608/004', + reference: 'https://attack.mitre.org/techniques/T1608/004/', tactics: ['resource-development'], techniqueId: 'T1608', value: 'driveByTarget', @@ -3997,7 +3997,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.004', name: 'Dylib Hijacking', - reference: 'https://attack.mitre.org/techniques/T1574/004', + reference: 'https://attack.mitre.org/techniques/T1574/004/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'dylibHijacking', @@ -4009,7 +4009,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.007', name: 'Dynamic API Resolution', - reference: 'https://attack.mitre.org/techniques/T1027/007', + reference: 'https://attack.mitre.org/techniques/T1027/007/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'dynamicApiResolution', @@ -4021,7 +4021,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1559.002', name: 'Dynamic Data Exchange', - reference: 'https://attack.mitre.org/techniques/T1559/002', + reference: 'https://attack.mitre.org/techniques/T1559/002/', tactics: ['execution'], techniqueId: 'T1559', value: 'dynamicDataExchange', @@ -4033,7 +4033,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.006', name: 'Dynamic Linker Hijacking', - reference: 'https://attack.mitre.org/techniques/T1574/006', + reference: 'https://attack.mitre.org/techniques/T1574/006/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'dynamicLinkerHijacking', @@ -4045,7 +4045,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.001', name: 'Dynamic-link Library Injection', - reference: 'https://attack.mitre.org/techniques/T1055/001', + reference: 'https://attack.mitre.org/techniques/T1055/001/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'dynamicLinkLibraryInjection', @@ -4057,7 +4057,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.015', name: 'Electron Applications', - reference: 'https://attack.mitre.org/techniques/T1218/015', + reference: 'https://attack.mitre.org/techniques/T1218/015/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'electronApplications', @@ -4069,7 +4069,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1548.004', name: 'Elevated Execution with Prompt', - reference: 'https://attack.mitre.org/techniques/T1548/004', + reference: 'https://attack.mitre.org/techniques/T1548/004/', tactics: ['privilege-escalation', 'defense-evasion'], techniqueId: 'T1548', value: 'elevatedExecutionWithPrompt', @@ -4081,7 +4081,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1087.003', name: 'Email Account', - reference: 'https://attack.mitre.org/techniques/T1087/003', + reference: 'https://attack.mitre.org/techniques/T1087/003/', tactics: ['discovery'], techniqueId: 'T1087', value: 'emailAccount', @@ -4093,7 +4093,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1586.002', name: 'Email Accounts', - reference: 'https://attack.mitre.org/techniques/T1586/002', + reference: 'https://attack.mitre.org/techniques/T1586/002/', tactics: ['resource-development'], techniqueId: 'T1586', value: 'emailAccounts', @@ -4105,7 +4105,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1585.002', name: 'Email Accounts', - reference: 'https://attack.mitre.org/techniques/T1585/002', + reference: 'https://attack.mitre.org/techniques/T1585/002/', tactics: ['resource-development'], techniqueId: 'T1585', value: 'emailAccounts', @@ -4117,7 +4117,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1589.002', name: 'Email Addresses', - reference: 'https://attack.mitre.org/techniques/T1589/002', + reference: 'https://attack.mitre.org/techniques/T1589/002/', tactics: ['reconnaissance'], techniqueId: 'T1589', value: 'emailAddresses', @@ -4129,7 +4129,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1114.003', name: 'Email Forwarding Rule', - reference: 'https://attack.mitre.org/techniques/T1114/003', + reference: 'https://attack.mitre.org/techniques/T1114/003/', tactics: ['collection'], techniqueId: 'T1114', value: 'emailForwardingRule', @@ -4141,7 +4141,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.008', name: 'Email Hiding Rules', - reference: 'https://attack.mitre.org/techniques/T1564/008', + reference: 'https://attack.mitre.org/techniques/T1564/008/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'emailHidingRules', @@ -4153,7 +4153,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.009', name: 'Embedded Payloads', - reference: 'https://attack.mitre.org/techniques/T1027/009', + reference: 'https://attack.mitre.org/techniques/T1027/009/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'embeddedPayloads', @@ -4165,7 +4165,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.014', name: 'Emond', - reference: 'https://attack.mitre.org/techniques/T1546/014', + reference: 'https://attack.mitre.org/techniques/T1546/014/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'emond', @@ -4177,7 +4177,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1589.003', name: 'Employee Names', - reference: 'https://attack.mitre.org/techniques/T1589/003', + reference: 'https://attack.mitre.org/techniques/T1589/003/', tactics: ['reconnaissance'], techniqueId: 'T1589', value: 'employeeNames', @@ -4189,7 +4189,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.013', name: 'Encrypted/Encoded File', - reference: 'https://attack.mitre.org/techniques/T1027/013', + reference: 'https://attack.mitre.org/techniques/T1027/013/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'encryptedEncodedFile', @@ -4201,7 +4201,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1480.001', name: 'Environmental Keying', - reference: 'https://attack.mitre.org/techniques/T1480/001', + reference: 'https://attack.mitre.org/techniques/T1480/001/', tactics: ['defense-evasion'], techniqueId: 'T1480', value: 'environmentalKeying', @@ -4213,7 +4213,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.005', name: 'Executable Installer File Permissions Weakness', - reference: 'https://attack.mitre.org/techniques/T1574/005', + reference: 'https://attack.mitre.org/techniques/T1574/005/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'executableInstallerFilePermissionsWeakness', @@ -4225,7 +4225,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1048.002', name: 'Exfiltration Over Asymmetric Encrypted Non-C2 Protocol', - reference: 'https://attack.mitre.org/techniques/T1048/002', + reference: 'https://attack.mitre.org/techniques/T1048/002/', tactics: ['exfiltration'], techniqueId: 'T1048', value: 'exfiltrationOverAsymmetricEncryptedNonC2Protocol', @@ -4237,7 +4237,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1011.001', name: 'Exfiltration Over Bluetooth', - reference: 'https://attack.mitre.org/techniques/T1011/001', + reference: 'https://attack.mitre.org/techniques/T1011/001/', tactics: ['exfiltration'], techniqueId: 'T1011', value: 'exfiltrationOverBluetooth', @@ -4249,7 +4249,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1048.001', name: 'Exfiltration Over Symmetric Encrypted Non-C2 Protocol', - reference: 'https://attack.mitre.org/techniques/T1048/001', + reference: 'https://attack.mitre.org/techniques/T1048/001/', tactics: ['exfiltration'], techniqueId: 'T1048', value: 'exfiltrationOverSymmetricEncryptedNonC2Protocol', @@ -4261,7 +4261,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1048.003', name: 'Exfiltration Over Unencrypted Non-C2 Protocol', - reference: 'https://attack.mitre.org/techniques/T1048/003', + reference: 'https://attack.mitre.org/techniques/T1048/003/', tactics: ['exfiltration'], techniqueId: 'T1048', value: 'exfiltrationOverUnencryptedNonC2Protocol', @@ -4273,7 +4273,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1567.004', name: 'Exfiltration Over Webhook', - reference: 'https://attack.mitre.org/techniques/T1567/004', + reference: 'https://attack.mitre.org/techniques/T1567/004/', tactics: ['exfiltration'], techniqueId: 'T1567', value: 'exfiltrationOverWebhook', @@ -4285,7 +4285,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1052.001', name: 'Exfiltration over USB', - reference: 'https://attack.mitre.org/techniques/T1052/001', + reference: 'https://attack.mitre.org/techniques/T1052/001/', tactics: ['exfiltration'], techniqueId: 'T1052', value: 'exfiltrationOverUsb', @@ -4297,7 +4297,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1567.002', name: 'Exfiltration to Cloud Storage', - reference: 'https://attack.mitre.org/techniques/T1567/002', + reference: 'https://attack.mitre.org/techniques/T1567/002/', tactics: ['exfiltration'], techniqueId: 'T1567', value: 'exfiltrationToCloudStorage', @@ -4309,7 +4309,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1567.001', name: 'Exfiltration to Code Repository', - reference: 'https://attack.mitre.org/techniques/T1567/001', + reference: 'https://attack.mitre.org/techniques/T1567/001/', tactics: ['exfiltration'], techniqueId: 'T1567', value: 'exfiltrationToCodeRepository', @@ -4321,7 +4321,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1567.003', name: 'Exfiltration to Text Storage Sites', - reference: 'https://attack.mitre.org/techniques/T1567/003', + reference: 'https://attack.mitre.org/techniques/T1567/003/', tactics: ['exfiltration'], techniqueId: 'T1567', value: 'exfiltrationToTextStorageSites', @@ -4333,7 +4333,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1587.004', name: 'Exploits', - reference: 'https://attack.mitre.org/techniques/T1587/004', + reference: 'https://attack.mitre.org/techniques/T1587/004/', tactics: ['resource-development'], techniqueId: 'T1587', value: 'exploits', @@ -4345,7 +4345,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.005', name: 'Exploits', - reference: 'https://attack.mitre.org/techniques/T1588/005', + reference: 'https://attack.mitre.org/techniques/T1588/005/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'exploits', @@ -4357,7 +4357,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1491.002', name: 'External Defacement', - reference: 'https://attack.mitre.org/techniques/T1491/002', + reference: 'https://attack.mitre.org/techniques/T1491/002/', tactics: ['impact'], techniqueId: 'T1491', value: 'externalDefacement', @@ -4369,7 +4369,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1090.002', name: 'External Proxy', - reference: 'https://attack.mitre.org/techniques/T1090/002', + reference: 'https://attack.mitre.org/techniques/T1090/002/', tactics: ['command-and-control'], techniqueId: 'T1090', value: 'externalProxy', @@ -4381,7 +4381,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.011', name: 'Extra Window Memory Injection', - reference: 'https://attack.mitre.org/techniques/T1055/011', + reference: 'https://attack.mitre.org/techniques/T1055/011/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'extraWindowMemoryInjection', @@ -4393,7 +4393,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1568.001', name: 'Fast Flux DNS', - reference: 'https://attack.mitre.org/techniques/T1568/001', + reference: 'https://attack.mitre.org/techniques/T1568/001/', tactics: ['command-and-control'], techniqueId: 'T1568', value: 'fastFluxDns', @@ -4405,7 +4405,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.004', name: 'File Deletion', - reference: 'https://attack.mitre.org/techniques/T1070/004', + reference: 'https://attack.mitre.org/techniques/T1070/004/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'fileDeletion', @@ -4417,7 +4417,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1071.002', name: 'File Transfer Protocols', - reference: 'https://attack.mitre.org/techniques/T1071/002', + reference: 'https://attack.mitre.org/techniques/T1071/002/', tactics: ['command-and-control'], techniqueId: 'T1071', value: 'fileTransferProtocols', @@ -4429,7 +4429,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.012', name: 'File/Path Exclusions', - reference: 'https://attack.mitre.org/techniques/T1564/012', + reference: 'https://attack.mitre.org/techniques/T1564/012/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'filePathExclusions', @@ -4441,7 +4441,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.011', name: 'Fileless Storage', - reference: 'https://attack.mitre.org/techniques/T1027/011', + reference: 'https://attack.mitre.org/techniques/T1027/011/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'filelessStorage', @@ -4453,7 +4453,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1592.003', name: 'Firmware', - reference: 'https://attack.mitre.org/techniques/T1592/003', + reference: 'https://attack.mitre.org/techniques/T1592/003/', tactics: ['reconnaissance'], techniqueId: 'T1592', value: 'firmware', @@ -4465,7 +4465,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1056.002', name: 'GUI Input Capture', - reference: 'https://attack.mitre.org/techniques/T1056/002', + reference: 'https://attack.mitre.org/techniques/T1056/002/', tactics: ['collection', 'credential-access'], techniqueId: 'T1056', value: 'guiInputCapture', @@ -4477,7 +4477,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1553.001', name: 'Gatekeeper Bypass', - reference: 'https://attack.mitre.org/techniques/T1553/001', + reference: 'https://attack.mitre.org/techniques/T1553/001/', tactics: ['defense-evasion'], techniqueId: 'T1553', value: 'gatekeeperBypass', @@ -4489,7 +4489,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1558.001', name: 'Golden Ticket', - reference: 'https://attack.mitre.org/techniques/T1558/001', + reference: 'https://attack.mitre.org/techniques/T1558/001/', tactics: ['credential-access'], techniqueId: 'T1558', value: 'goldenTicket', @@ -4501,7 +4501,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1484.001', name: 'Group Policy Modification', - reference: 'https://attack.mitre.org/techniques/T1484/001', + reference: 'https://attack.mitre.org/techniques/T1484/001/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1484', value: 'groupPolicyModification', @@ -4513,7 +4513,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.006', name: 'Group Policy Preferences', - reference: 'https://attack.mitre.org/techniques/T1552/006', + reference: 'https://attack.mitre.org/techniques/T1552/006/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'groupPolicyPreferences', @@ -4525,7 +4525,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.006', name: 'HTML Smuggling', - reference: 'https://attack.mitre.org/techniques/T1027/006', + reference: 'https://attack.mitre.org/techniques/T1027/006/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'htmlSmuggling', @@ -4537,7 +4537,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1592.001', name: 'Hardware', - reference: 'https://attack.mitre.org/techniques/T1592/001', + reference: 'https://attack.mitre.org/techniques/T1592/001/', tactics: ['reconnaissance'], techniqueId: 'T1592', value: 'hardware', @@ -4549,7 +4549,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.005', name: 'Hidden File System', - reference: 'https://attack.mitre.org/techniques/T1564/005', + reference: 'https://attack.mitre.org/techniques/T1564/005/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'hiddenFileSystem', @@ -4561,7 +4561,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.001', name: 'Hidden Files and Directories', - reference: 'https://attack.mitre.org/techniques/T1564/001', + reference: 'https://attack.mitre.org/techniques/T1564/001/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'hiddenFilesAndDirectories', @@ -4573,7 +4573,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.002', name: 'Hidden Users', - reference: 'https://attack.mitre.org/techniques/T1564/002', + reference: 'https://attack.mitre.org/techniques/T1564/002/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'hiddenUsers', @@ -4585,7 +4585,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.003', name: 'Hidden Window', - reference: 'https://attack.mitre.org/techniques/T1564/003', + reference: 'https://attack.mitre.org/techniques/T1564/003/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'hiddenWindow', @@ -4597,7 +4597,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.007', name: 'Hybrid Identity', - reference: 'https://attack.mitre.org/techniques/T1556/007', + reference: 'https://attack.mitre.org/techniques/T1556/007/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'hybridIdentity', @@ -4609,7 +4609,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1505.004', name: 'IIS Components', - reference: 'https://attack.mitre.org/techniques/T1505/004', + reference: 'https://attack.mitre.org/techniques/T1505/004/', tactics: ['persistence'], techniqueId: 'T1505', value: 'iisComponents', @@ -4621,7 +4621,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1590.005', name: 'IP Addresses', - reference: 'https://attack.mitre.org/techniques/T1590/005', + reference: 'https://attack.mitre.org/techniques/T1590/005/', tactics: ['reconnaissance'], techniqueId: 'T1590', value: 'ipAddresses', @@ -4633,7 +4633,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1591.003', name: 'Identify Business Tempo', - reference: 'https://attack.mitre.org/techniques/T1591/003', + reference: 'https://attack.mitre.org/techniques/T1591/003/', tactics: ['reconnaissance'], techniqueId: 'T1591', value: 'identifyBusinessTempo', @@ -4645,7 +4645,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1591.004', name: 'Identify Roles', - reference: 'https://attack.mitre.org/techniques/T1591/004', + reference: 'https://attack.mitre.org/techniques/T1591/004/', tactics: ['reconnaissance'], techniqueId: 'T1591', value: 'identifyRoles', @@ -4657,7 +4657,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.011', name: 'Ignore Process Interrupts', - reference: 'https://attack.mitre.org/techniques/T1564/011', + reference: 'https://attack.mitre.org/techniques/T1564/011/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'ignoreProcessInterrupts', @@ -4669,7 +4669,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.012', name: 'Image File Execution Options Injection', - reference: 'https://attack.mitre.org/techniques/T1546/012', + reference: 'https://attack.mitre.org/techniques/T1546/012/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'imageFileExecutionOptionsInjection', @@ -4681,7 +4681,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.003', name: 'Impair Command History Logging', - reference: 'https://attack.mitre.org/techniques/T1562/003', + reference: 'https://attack.mitre.org/techniques/T1562/003/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'impairCommandHistoryLogging', @@ -4693,7 +4693,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.006', name: 'Indicator Blocking', - reference: 'https://attack.mitre.org/techniques/T1562/006', + reference: 'https://attack.mitre.org/techniques/T1562/006/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'indicatorBlocking', @@ -4705,7 +4705,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.005', name: 'Indicator Removal from Tools', - reference: 'https://attack.mitre.org/techniques/T1027/005', + reference: 'https://attack.mitre.org/techniques/T1027/005/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'indicatorRemovalFromTools', @@ -4717,7 +4717,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1608.003', name: 'Install Digital Certificate', - reference: 'https://attack.mitre.org/techniques/T1608/003', + reference: 'https://attack.mitre.org/techniques/T1608/003/', tactics: ['resource-development'], techniqueId: 'T1608', value: 'installDigitalCertificate', @@ -4729,7 +4729,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1553.004', name: 'Install Root Certificate', - reference: 'https://attack.mitre.org/techniques/T1553/004', + reference: 'https://attack.mitre.org/techniques/T1553/004/', tactics: ['defense-evasion'], techniqueId: 'T1553', value: 'installRootCertificate', @@ -4741,7 +4741,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.004', name: 'InstallUtil', - reference: 'https://attack.mitre.org/techniques/T1218/004', + reference: 'https://attack.mitre.org/techniques/T1218/004/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'installUtil', @@ -4753,7 +4753,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.016', name: 'Installer Packages', - reference: 'https://attack.mitre.org/techniques/T1546/016', + reference: 'https://attack.mitre.org/techniques/T1546/016/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'installerPackages', @@ -4765,7 +4765,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1491.001', name: 'Internal Defacement', - reference: 'https://attack.mitre.org/techniques/T1491/001', + reference: 'https://attack.mitre.org/techniques/T1491/001/', tactics: ['impact'], techniqueId: 'T1491', value: 'internalDefacement', @@ -4777,7 +4777,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1090.001', name: 'Internal Proxy', - reference: 'https://attack.mitre.org/techniques/T1090/001', + reference: 'https://attack.mitre.org/techniques/T1090/001/', tactics: ['command-and-control'], techniqueId: 'T1090', value: 'internalProxy', @@ -4789,7 +4789,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1016.001', name: 'Internet Connection Discovery', - reference: 'https://attack.mitre.org/techniques/T1016/001', + reference: 'https://attack.mitre.org/techniques/T1016/001/', tactics: ['discovery'], techniqueId: 'T1016', value: 'internetConnectionDiscovery', @@ -4801,7 +4801,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.001', name: 'Invalid Code Signature', - reference: 'https://attack.mitre.org/techniques/T1036/001', + reference: 'https://attack.mitre.org/techniques/T1036/001/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'invalidCodeSignature', @@ -4813,7 +4813,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.007', name: 'JavaScript', - reference: 'https://attack.mitre.org/techniques/T1059/007', + reference: 'https://attack.mitre.org/techniques/T1059/007/', tactics: ['execution'], techniqueId: 'T1059', value: 'javaScript', @@ -4825,7 +4825,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1001.001', name: 'Junk Data', - reference: 'https://attack.mitre.org/techniques/T1001/001', + reference: 'https://attack.mitre.org/techniques/T1001/001/', tactics: ['command-and-control'], techniqueId: 'T1001', value: 'junkData', @@ -4837,7 +4837,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1558.003', name: 'Kerberoasting', - reference: 'https://attack.mitre.org/techniques/T1558/003', + reference: 'https://attack.mitre.org/techniques/T1558/003/', tactics: ['credential-access'], techniqueId: 'T1558', value: 'kerberoasting', @@ -4849,7 +4849,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.006', name: 'Kernel Modules and Extensions', - reference: 'https://attack.mitre.org/techniques/T1547/006', + reference: 'https://attack.mitre.org/techniques/T1547/006/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'kernelModulesAndExtensions', @@ -4861,7 +4861,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.013', name: 'KernelCallbackTable', - reference: 'https://attack.mitre.org/techniques/T1574/013', + reference: 'https://attack.mitre.org/techniques/T1574/013/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'kernelCallbackTable', @@ -4873,7 +4873,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1555.001', name: 'Keychain', - reference: 'https://attack.mitre.org/techniques/T1555/001', + reference: 'https://attack.mitre.org/techniques/T1555/001/', tactics: ['credential-access'], techniqueId: 'T1555', value: 'keychain', @@ -4885,7 +4885,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1056.001', name: 'Keylogging', - reference: 'https://attack.mitre.org/techniques/T1056/001', + reference: 'https://attack.mitre.org/techniques/T1056/001/', tactics: ['collection', 'credential-access'], techniqueId: 'T1056', value: 'keylogging', @@ -4897,7 +4897,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.006', name: 'LC_LOAD_DYLIB Addition', - reference: 'https://attack.mitre.org/techniques/T1546/006', + reference: 'https://attack.mitre.org/techniques/T1546/006/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'lcLoadDylibAddition', @@ -4909,7 +4909,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1557.001', name: 'LLMNR/NBT-NS Poisoning and SMB Relay', - reference: 'https://attack.mitre.org/techniques/T1557/001', + reference: 'https://attack.mitre.org/techniques/T1557/001/', tactics: ['credential-access', 'collection'], techniqueId: 'T1557', value: 'llmnrNbtNsPoisoningAndSmbRelay', @@ -4921,7 +4921,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.012', name: 'LNK Icon Smuggling', - reference: 'https://attack.mitre.org/techniques/T1027/012', + reference: 'https://attack.mitre.org/techniques/T1027/012/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'lnkIconSmuggling', @@ -4933,7 +4933,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.004', name: 'LSA Secrets', - reference: 'https://attack.mitre.org/techniques/T1003/004', + reference: 'https://attack.mitre.org/techniques/T1003/004/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'lsaSecrets', @@ -4945,7 +4945,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.008', name: 'LSASS Driver', - reference: 'https://attack.mitre.org/techniques/T1547/008', + reference: 'https://attack.mitre.org/techniques/T1547/008/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'lsassDriver', @@ -4957,7 +4957,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.001', name: 'LSASS Memory', - reference: 'https://attack.mitre.org/techniques/T1003/001', + reference: 'https://attack.mitre.org/techniques/T1003/001/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'lsassMemory', @@ -4969,7 +4969,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1543.001', name: 'Launch Agent', - reference: 'https://attack.mitre.org/techniques/T1543/001', + reference: 'https://attack.mitre.org/techniques/T1543/001/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1543', value: 'launchAgent', @@ -4981,7 +4981,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1543.004', name: 'Launch Daemon', - reference: 'https://attack.mitre.org/techniques/T1543/004', + reference: 'https://attack.mitre.org/techniques/T1543/004/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1543', value: 'launchDaemon', @@ -4993,7 +4993,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1569.001', name: 'Launchctl', - reference: 'https://attack.mitre.org/techniques/T1569/001', + reference: 'https://attack.mitre.org/techniques/T1569/001/', tactics: ['execution'], techniqueId: 'T1569', value: 'launchctl', @@ -5005,7 +5005,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1608.005', name: 'Link Target', - reference: 'https://attack.mitre.org/techniques/T1608/005', + reference: 'https://attack.mitre.org/techniques/T1608/005/', tactics: ['resource-development'], techniqueId: 'T1608', value: 'linkTarget', @@ -5017,7 +5017,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1222.002', name: 'Linux and Mac File and Directory Permissions Modification', - reference: 'https://attack.mitre.org/techniques/T1222/002', + reference: 'https://attack.mitre.org/techniques/T1222/002/', tactics: ['defense-evasion'], techniqueId: 'T1222', value: 'linuxAndMacFileAndDirectoryPermissionsModification', @@ -5029,7 +5029,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.015', name: 'ListPlanting', - reference: 'https://attack.mitre.org/techniques/T1055/015', + reference: 'https://attack.mitre.org/techniques/T1055/015/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'listPlanting', @@ -5041,7 +5041,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1087.001', name: 'Local Account', - reference: 'https://attack.mitre.org/techniques/T1087/001', + reference: 'https://attack.mitre.org/techniques/T1087/001/', tactics: ['discovery'], techniqueId: 'T1087', value: 'localAccount', @@ -5053,7 +5053,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1136.001', name: 'Local Account', - reference: 'https://attack.mitre.org/techniques/T1136/001', + reference: 'https://attack.mitre.org/techniques/T1136/001/', tactics: ['persistence'], techniqueId: 'T1136', value: 'localAccount', @@ -5065,7 +5065,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1078.003', name: 'Local Accounts', - reference: 'https://attack.mitre.org/techniques/T1078/003', + reference: 'https://attack.mitre.org/techniques/T1078/003/', tactics: ['defense-evasion', 'persistence', 'privilege-escalation', 'initial-access'], techniqueId: 'T1078', value: 'localAccounts', @@ -5077,7 +5077,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1074.001', name: 'Local Data Staging', - reference: 'https://attack.mitre.org/techniques/T1074/001', + reference: 'https://attack.mitre.org/techniques/T1074/001/', tactics: ['collection'], techniqueId: 'T1074', value: 'localDataStaging', @@ -5089,7 +5089,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1114.001', name: 'Local Email Collection', - reference: 'https://attack.mitre.org/techniques/T1114/001', + reference: 'https://attack.mitre.org/techniques/T1114/001/', tactics: ['collection'], techniqueId: 'T1114', value: 'localEmailCollection', @@ -5101,7 +5101,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1069.001', name: 'Local Groups', - reference: 'https://attack.mitre.org/techniques/T1069/001', + reference: 'https://attack.mitre.org/techniques/T1069/001/', tactics: ['discovery'], techniqueId: 'T1069', value: 'localGroups', @@ -5113,7 +5113,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1037.002', name: 'Login Hook', - reference: 'https://attack.mitre.org/techniques/T1037/002', + reference: 'https://attack.mitre.org/techniques/T1037/002/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1037', value: 'loginHook', @@ -5125,7 +5125,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.015', name: 'Login Items', - reference: 'https://attack.mitre.org/techniques/T1547/015', + reference: 'https://attack.mitre.org/techniques/T1547/015/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'loginItems', @@ -5137,7 +5137,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1037.001', name: 'Logon Script (Windows)', - reference: 'https://attack.mitre.org/techniques/T1037/001', + reference: 'https://attack.mitre.org/techniques/T1037/001/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1037', value: 'logonScriptWindows', @@ -5149,7 +5149,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.014', name: 'MMC', - reference: 'https://attack.mitre.org/techniques/T1218/014', + reference: 'https://attack.mitre.org/techniques/T1218/014/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'mmc', @@ -5161,7 +5161,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1127.001', name: 'MSBuild', - reference: 'https://attack.mitre.org/techniques/T1127/001', + reference: 'https://attack.mitre.org/techniques/T1127/001/', tactics: ['defense-evasion'], techniqueId: 'T1127', value: 'msBuild', @@ -5173,7 +5173,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1071.003', name: 'Mail Protocols', - reference: 'https://attack.mitre.org/techniques/T1071/003', + reference: 'https://attack.mitre.org/techniques/T1071/003/', tactics: ['command-and-control'], techniqueId: 'T1071', value: 'mailProtocols', @@ -5185,7 +5185,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1134.003', name: 'Make and Impersonate Token', - reference: 'https://attack.mitre.org/techniques/T1134/003', + reference: 'https://attack.mitre.org/techniques/T1134/003/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1134', value: 'makeAndImpersonateToken', @@ -5197,7 +5197,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1204.002', name: 'Malicious File', - reference: 'https://attack.mitre.org/techniques/T1204/002', + reference: 'https://attack.mitre.org/techniques/T1204/002/', tactics: ['execution'], techniqueId: 'T1204', value: 'maliciousFile', @@ -5209,7 +5209,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1204.003', name: 'Malicious Image', - reference: 'https://attack.mitre.org/techniques/T1204/003', + reference: 'https://attack.mitre.org/techniques/T1204/003/', tactics: ['execution'], techniqueId: 'T1204', value: 'maliciousImage', @@ -5221,7 +5221,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1204.001', name: 'Malicious Link', - reference: 'https://attack.mitre.org/techniques/T1204/001', + reference: 'https://attack.mitre.org/techniques/T1204/001/', tactics: ['execution'], techniqueId: 'T1204', value: 'maliciousLink', @@ -5233,7 +5233,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.008', name: 'Malvertising', - reference: 'https://attack.mitre.org/techniques/T1583/008', + reference: 'https://attack.mitre.org/techniques/T1583/008/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'malvertising', @@ -5245,7 +5245,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1587.001', name: 'Malware', - reference: 'https://attack.mitre.org/techniques/T1587/001', + reference: 'https://attack.mitre.org/techniques/T1587/001/', tactics: ['resource-development'], techniqueId: 'T1587', value: 'malware', @@ -5257,7 +5257,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.001', name: 'Malware', - reference: 'https://attack.mitre.org/techniques/T1588/001', + reference: 'https://attack.mitre.org/techniques/T1588/001/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'malware', @@ -5269,7 +5269,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1553.005', name: 'Mark-of-the-Web Bypass', - reference: 'https://attack.mitre.org/techniques/T1553/005', + reference: 'https://attack.mitre.org/techniques/T1553/005/', tactics: ['defense-evasion'], techniqueId: 'T1553', value: 'markOfTheWebBypass', @@ -5281,7 +5281,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.008', name: 'Masquerade File Type', - reference: 'https://attack.mitre.org/techniques/T1036/008', + reference: 'https://attack.mitre.org/techniques/T1036/008/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'masqueradeFileType', @@ -5293,7 +5293,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.004', name: 'Masquerade Task or Service', - reference: 'https://attack.mitre.org/techniques/T1036/004', + reference: 'https://attack.mitre.org/techniques/T1036/004/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'masqueradeTaskOrService', @@ -5305,7 +5305,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.005', name: 'Match Legitimate Name or Location', - reference: 'https://attack.mitre.org/techniques/T1036/005', + reference: 'https://attack.mitre.org/techniques/T1036/005/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'matchLegitimateNameOrLocation', @@ -5317,7 +5317,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.013', name: 'Mavinject', - reference: 'https://attack.mitre.org/techniques/T1218/013', + reference: 'https://attack.mitre.org/techniques/T1218/013/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'mavinject', @@ -5329,7 +5329,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1578.005', name: 'Modify Cloud Compute Configurations', - reference: 'https://attack.mitre.org/techniques/T1578/005', + reference: 'https://attack.mitre.org/techniques/T1578/005/', tactics: ['defense-evasion'], techniqueId: 'T1578', value: 'modifyCloudComputeConfigurations', @@ -5341,7 +5341,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.005', name: 'Mshta', - reference: 'https://attack.mitre.org/techniques/T1218/005', + reference: 'https://attack.mitre.org/techniques/T1218/005/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'mshta', @@ -5353,7 +5353,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.007', name: 'Msiexec', - reference: 'https://attack.mitre.org/techniques/T1218/007', + reference: 'https://attack.mitre.org/techniques/T1218/007/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'msiexec', @@ -5365,7 +5365,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.006', name: 'Multi-Factor Authentication', - reference: 'https://attack.mitre.org/techniques/T1556/006', + reference: 'https://attack.mitre.org/techniques/T1556/006/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'multiFactorAuthentication', @@ -5377,7 +5377,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1090.003', name: 'Multi-hop Proxy', - reference: 'https://attack.mitre.org/techniques/T1090/003', + reference: 'https://attack.mitre.org/techniques/T1090/003/', tactics: ['command-and-control'], techniqueId: 'T1090', value: 'multiHopProxy', @@ -5389,7 +5389,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.003', name: 'NTDS', - reference: 'https://attack.mitre.org/techniques/T1003/003', + reference: 'https://attack.mitre.org/techniques/T1003/003/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'ntds', @@ -5401,7 +5401,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.004', name: 'NTFS File Attributes', - reference: 'https://attack.mitre.org/techniques/T1564/004', + reference: 'https://attack.mitre.org/techniques/T1564/004/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'ntfsFileAttributes', @@ -5413,7 +5413,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.007', name: 'Netsh Helper DLL', - reference: 'https://attack.mitre.org/techniques/T1546/007', + reference: 'https://attack.mitre.org/techniques/T1546/007/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'netshHelperDll', @@ -5425,7 +5425,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1599.001', name: 'Network Address Translation Traversal', - reference: 'https://attack.mitre.org/techniques/T1599/001', + reference: 'https://attack.mitre.org/techniques/T1599/001/', tactics: ['defense-evasion'], techniqueId: 'T1599', value: 'networkAddressTranslationTraversal', @@ -5437,7 +5437,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.004', name: 'Network Device Authentication', - reference: 'https://attack.mitre.org/techniques/T1556/004', + reference: 'https://attack.mitre.org/techniques/T1556/004/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'networkDeviceAuthentication', @@ -5449,7 +5449,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.008', name: 'Network Device CLI', - reference: 'https://attack.mitre.org/techniques/T1059/008', + reference: 'https://attack.mitre.org/techniques/T1059/008/', tactics: ['execution'], techniqueId: 'T1059', value: 'networkDeviceCli', @@ -5461,7 +5461,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1602.002', name: 'Network Device Configuration Dump', - reference: 'https://attack.mitre.org/techniques/T1602/002', + reference: 'https://attack.mitre.org/techniques/T1602/002/', tactics: ['collection'], techniqueId: 'T1602', value: 'networkDeviceConfigurationDump', @@ -5473,7 +5473,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.008', name: 'Network Devices', - reference: 'https://attack.mitre.org/techniques/T1584/008', + reference: 'https://attack.mitre.org/techniques/T1584/008/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'networkDevices', @@ -5485,7 +5485,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1037.003', name: 'Network Logon Script', - reference: 'https://attack.mitre.org/techniques/T1037/003', + reference: 'https://attack.mitre.org/techniques/T1037/003/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1037', value: 'networkLogonScript', @@ -5497,7 +5497,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.008', name: 'Network Provider DLL', - reference: 'https://attack.mitre.org/techniques/T1556/008', + reference: 'https://attack.mitre.org/techniques/T1556/008/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'networkProviderDll', @@ -5509,7 +5509,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1590.006', name: 'Network Security Appliances', - reference: 'https://attack.mitre.org/techniques/T1590/006', + reference: 'https://attack.mitre.org/techniques/T1590/006/', tactics: ['reconnaissance'], techniqueId: 'T1590', value: 'networkSecurityAppliances', @@ -5521,7 +5521,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.005', name: 'Network Share Connection Removal', - reference: 'https://attack.mitre.org/techniques/T1070/005', + reference: 'https://attack.mitre.org/techniques/T1070/005/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'networkShareConnectionRemoval', @@ -5533,7 +5533,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1590.004', name: 'Network Topology', - reference: 'https://attack.mitre.org/techniques/T1590/004', + reference: 'https://attack.mitre.org/techniques/T1590/004/', tactics: ['reconnaissance'], techniqueId: 'T1590', value: 'networkTopology', @@ -5545,7 +5545,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1590.003', name: 'Network Trust Dependencies', - reference: 'https://attack.mitre.org/techniques/T1590/003', + reference: 'https://attack.mitre.org/techniques/T1590/003/', tactics: ['reconnaissance'], techniqueId: 'T1590', value: 'networkTrustDependencies', @@ -5557,7 +5557,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1132.002', name: 'Non-Standard Encoding', - reference: 'https://attack.mitre.org/techniques/T1132/002', + reference: 'https://attack.mitre.org/techniques/T1132/002/', tactics: ['command-and-control'], techniqueId: 'T1132', value: 'nonStandardEncoding', @@ -5569,7 +5569,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1499.001', name: 'OS Exhaustion Flood', - reference: 'https://attack.mitre.org/techniques/T1499/001', + reference: 'https://attack.mitre.org/techniques/T1499/001/', tactics: ['impact'], techniqueId: 'T1499', value: 'osExhaustionFlood', @@ -5581,7 +5581,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.008', name: 'Odbcconf', - reference: 'https://attack.mitre.org/techniques/T1218/008', + reference: 'https://attack.mitre.org/techniques/T1218/008/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'odbcconf', @@ -5593,7 +5593,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1137.001', name: 'Office Template Macros', - reference: 'https://attack.mitre.org/techniques/T1137/001', + reference: 'https://attack.mitre.org/techniques/T1137/001/', tactics: ['persistence'], techniqueId: 'T1137', value: 'officeTemplateMacros', @@ -5605,7 +5605,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1137.002', name: 'Office Test', - reference: 'https://attack.mitre.org/techniques/T1137/002', + reference: 'https://attack.mitre.org/techniques/T1137/002/', tactics: ['persistence'], techniqueId: 'T1137', value: 'officeTest', @@ -5617,7 +5617,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1102.003', name: 'One-Way Communication', - reference: 'https://attack.mitre.org/techniques/T1102/003', + reference: 'https://attack.mitre.org/techniques/T1102/003/', tactics: ['command-and-control'], techniqueId: 'T1102', value: 'oneWayCommunication', @@ -5629,7 +5629,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1137.003', name: 'Outlook Forms', - reference: 'https://attack.mitre.org/techniques/T1137/003', + reference: 'https://attack.mitre.org/techniques/T1137/003/', tactics: ['persistence'], techniqueId: 'T1137', value: 'outlookForms', @@ -5641,7 +5641,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1137.004', name: 'Outlook Home Page', - reference: 'https://attack.mitre.org/techniques/T1137/004', + reference: 'https://attack.mitre.org/techniques/T1137/004/', tactics: ['persistence'], techniqueId: 'T1137', value: 'outlookHomePage', @@ -5653,7 +5653,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1137.005', name: 'Outlook Rules', - reference: 'https://attack.mitre.org/techniques/T1137/005', + reference: 'https://attack.mitre.org/techniques/T1137/005/', tactics: ['persistence'], techniqueId: 'T1137', value: 'outlookRules', @@ -5665,7 +5665,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1134.004', name: 'Parent PID Spoofing', - reference: 'https://attack.mitre.org/techniques/T1134/004', + reference: 'https://attack.mitre.org/techniques/T1134/004/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1134', value: 'parentPidSpoofing', @@ -5677,7 +5677,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1550.002', name: 'Pass the Hash', - reference: 'https://attack.mitre.org/techniques/T1550/002', + reference: 'https://attack.mitre.org/techniques/T1550/002/', tactics: ['defense-evasion', 'lateral-movement'], techniqueId: 'T1550', value: 'passTheHash', @@ -5689,7 +5689,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1550.003', name: 'Pass the Ticket', - reference: 'https://attack.mitre.org/techniques/T1550/003', + reference: 'https://attack.mitre.org/techniques/T1550/003/', tactics: ['defense-evasion', 'lateral-movement'], techniqueId: 'T1550', value: 'passTheTicket', @@ -5701,7 +5701,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1110.002', name: 'Password Cracking', - reference: 'https://attack.mitre.org/techniques/T1110/002', + reference: 'https://attack.mitre.org/techniques/T1110/002/', tactics: ['credential-access'], techniqueId: 'T1110', value: 'passwordCracking', @@ -5713,7 +5713,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.002', name: 'Password Filter DLL', - reference: 'https://attack.mitre.org/techniques/T1556/002', + reference: 'https://attack.mitre.org/techniques/T1556/002/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'passwordFilterDll', @@ -5725,7 +5725,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1110.001', name: 'Password Guessing', - reference: 'https://attack.mitre.org/techniques/T1110/001', + reference: 'https://attack.mitre.org/techniques/T1110/001/', tactics: ['credential-access'], techniqueId: 'T1110', value: 'passwordGuessing', @@ -5737,7 +5737,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1555.005', name: 'Password Managers', - reference: 'https://attack.mitre.org/techniques/T1555/005', + reference: 'https://attack.mitre.org/techniques/T1555/005/', tactics: ['credential-access'], techniqueId: 'T1555', value: 'passwordManagers', @@ -5749,7 +5749,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1110.003', name: 'Password Spraying', - reference: 'https://attack.mitre.org/techniques/T1110/003', + reference: 'https://attack.mitre.org/techniques/T1110/003/', tactics: ['credential-access'], techniqueId: 'T1110', value: 'passwordSpraying', @@ -5761,7 +5761,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1601.001', name: 'Patch System Image', - reference: 'https://attack.mitre.org/techniques/T1601/001', + reference: 'https://attack.mitre.org/techniques/T1601/001/', tactics: ['defense-evasion'], techniqueId: 'T1601', value: 'patchSystemImage', @@ -5773,7 +5773,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.007', name: 'Path Interception by PATH Environment Variable', - reference: 'https://attack.mitre.org/techniques/T1574/007', + reference: 'https://attack.mitre.org/techniques/T1574/007/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'pathInterceptionByPathEnvironmentVariable', @@ -5785,7 +5785,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.008', name: 'Path Interception by Search Order Hijacking', - reference: 'https://attack.mitre.org/techniques/T1574/008', + reference: 'https://attack.mitre.org/techniques/T1574/008/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'pathInterceptionBySearchOrderHijacking', @@ -5797,7 +5797,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.009', name: 'Path Interception by Unquoted Path', - reference: 'https://attack.mitre.org/techniques/T1574/009', + reference: 'https://attack.mitre.org/techniques/T1574/009/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'pathInterceptionByUnquotedPath', @@ -5809,7 +5809,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.003', name: 'Pluggable Authentication Modules', - reference: 'https://attack.mitre.org/techniques/T1556/003', + reference: 'https://attack.mitre.org/techniques/T1556/003/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'pluggableAuthenticationModules', @@ -5821,7 +5821,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1205.001', name: 'Port Knocking', - reference: 'https://attack.mitre.org/techniques/T1205/001', + reference: 'https://attack.mitre.org/techniques/T1205/001/', tactics: ['defense-evasion', 'persistence', 'command-and-control'], techniqueId: 'T1205', value: 'portKnocking', @@ -5833,7 +5833,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.010', name: 'Port Monitors', - reference: 'https://attack.mitre.org/techniques/T1547/010', + reference: 'https://attack.mitre.org/techniques/T1547/010/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'portMonitors', @@ -5845,7 +5845,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.002', name: 'Portable Executable Injection', - reference: 'https://attack.mitre.org/techniques/T1055/002', + reference: 'https://attack.mitre.org/techniques/T1055/002/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'portableExecutableInjection', @@ -5857,7 +5857,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.001', name: 'PowerShell', - reference: 'https://attack.mitre.org/techniques/T1059/001', + reference: 'https://attack.mitre.org/techniques/T1059/001/', tactics: ['execution'], techniqueId: 'T1059', value: 'powerShell', @@ -5869,7 +5869,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.013', name: 'PowerShell Profile', - reference: 'https://attack.mitre.org/techniques/T1546/013', + reference: 'https://attack.mitre.org/techniques/T1546/013/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'powerShellProfile', @@ -5881,7 +5881,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.012', name: 'Print Processors', - reference: 'https://attack.mitre.org/techniques/T1547/012', + reference: 'https://attack.mitre.org/techniques/T1547/012/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'printProcessors', @@ -5893,7 +5893,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1552.004', name: 'Private Keys', - reference: 'https://attack.mitre.org/techniques/T1552/004', + reference: 'https://attack.mitre.org/techniques/T1552/004/', tactics: ['credential-access'], techniqueId: 'T1552', value: 'privateKeys', @@ -5905,7 +5905,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.007', name: 'Proc Filesystem', - reference: 'https://attack.mitre.org/techniques/T1003/007', + reference: 'https://attack.mitre.org/techniques/T1003/007/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'procFilesystem', @@ -5917,7 +5917,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.009', name: 'Proc Memory', - reference: 'https://attack.mitre.org/techniques/T1055/009', + reference: 'https://attack.mitre.org/techniques/T1055/009/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'procMemory', @@ -5929,7 +5929,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.010', name: 'Process Argument Spoofing', - reference: 'https://attack.mitre.org/techniques/T1564/010', + reference: 'https://attack.mitre.org/techniques/T1564/010/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'processArgumentSpoofing', @@ -5941,7 +5941,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.013', name: 'Process Doppelgänging', - reference: 'https://attack.mitre.org/techniques/T1055/013', + reference: 'https://attack.mitre.org/techniques/T1055/013/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'processDoppelganging', @@ -5953,7 +5953,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.012', name: 'Process Hollowing', - reference: 'https://attack.mitre.org/techniques/T1055/012', + reference: 'https://attack.mitre.org/techniques/T1055/012/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'processHollowing', @@ -5965,7 +5965,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1001.003', name: 'Protocol Impersonation', - reference: 'https://attack.mitre.org/techniques/T1001/003', + reference: 'https://attack.mitre.org/techniques/T1001/003/', tactics: ['command-and-control'], techniqueId: 'T1001', value: 'protocolImpersonation', @@ -5977,7 +5977,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.008', name: 'Ptrace System Calls', - reference: 'https://attack.mitre.org/techniques/T1055/008', + reference: 'https://attack.mitre.org/techniques/T1055/008/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'ptraceSystemCalls', @@ -5989,7 +5989,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1216.001', name: 'PubPrn', - reference: 'https://attack.mitre.org/techniques/T1216/001', + reference: 'https://attack.mitre.org/techniques/T1216/001/', tactics: ['defense-evasion'], techniqueId: 'T1216', value: 'pubPrn', @@ -6001,7 +6001,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1597.002', name: 'Purchase Technical Data', - reference: 'https://attack.mitre.org/techniques/T1597/002', + reference: 'https://attack.mitre.org/techniques/T1597/002/', tactics: ['reconnaissance'], techniqueId: 'T1597', value: 'purchaseTechnicalData', @@ -6013,7 +6013,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.006', name: 'Python', - reference: 'https://attack.mitre.org/techniques/T1059/006', + reference: 'https://attack.mitre.org/techniques/T1059/006/', tactics: ['execution'], techniqueId: 'T1059', value: 'python', @@ -6025,7 +6025,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1037.004', name: 'RC Scripts', - reference: 'https://attack.mitre.org/techniques/T1037/004', + reference: 'https://attack.mitre.org/techniques/T1037/004/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1037', value: 'rcScripts', @@ -6037,7 +6037,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1563.002', name: 'RDP Hijacking', - reference: 'https://attack.mitre.org/techniques/T1563/002', + reference: 'https://attack.mitre.org/techniques/T1563/002/', tactics: ['lateral-movement'], techniqueId: 'T1563', value: 'rdpHijacking', @@ -6049,7 +6049,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1542.004', name: 'ROMMONkit', - reference: 'https://attack.mitre.org/techniques/T1542/004', + reference: 'https://attack.mitre.org/techniques/T1542/004/', tactics: ['defense-evasion', 'persistence'], techniqueId: 'T1542', value: 'rommoNkit', @@ -6061,7 +6061,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.007', name: 'Re-opened Applications', - reference: 'https://attack.mitre.org/techniques/T1547/007', + reference: 'https://attack.mitre.org/techniques/T1547/007/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'reOpenedApplications', @@ -6073,7 +6073,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1600.001', name: 'Reduce Key Space', - reference: 'https://attack.mitre.org/techniques/T1600/001', + reference: 'https://attack.mitre.org/techniques/T1600/001/', tactics: ['defense-evasion'], techniqueId: 'T1600', value: 'reduceKeySpace', @@ -6085,7 +6085,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1498.002', name: 'Reflection Amplification', - reference: 'https://attack.mitre.org/techniques/T1498/002', + reference: 'https://attack.mitre.org/techniques/T1498/002/', tactics: ['impact'], techniqueId: 'T1498', value: 'reflectionAmplification', @@ -6097,7 +6097,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.001', name: 'Registry Run Keys / Startup Folder', - reference: 'https://attack.mitre.org/techniques/T1547/001', + reference: 'https://attack.mitre.org/techniques/T1547/001/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'registryRunKeysStartupFolder', @@ -6109,7 +6109,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.009', name: 'Regsvcs/Regasm', - reference: 'https://attack.mitre.org/techniques/T1218/009', + reference: 'https://attack.mitre.org/techniques/T1218/009/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'regsvcsRegasm', @@ -6121,7 +6121,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.010', name: 'Regsvr32', - reference: 'https://attack.mitre.org/techniques/T1218/010', + reference: 'https://attack.mitre.org/techniques/T1218/010/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'regsvr32', @@ -6133,7 +6133,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1074.002', name: 'Remote Data Staging', - reference: 'https://attack.mitre.org/techniques/T1074/002', + reference: 'https://attack.mitre.org/techniques/T1074/002/', tactics: ['collection'], techniqueId: 'T1074', value: 'remoteDataStaging', @@ -6145,7 +6145,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.001', name: 'Remote Desktop Protocol', - reference: 'https://attack.mitre.org/techniques/T1021/001', + reference: 'https://attack.mitre.org/techniques/T1021/001/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'remoteDesktopProtocol', @@ -6157,7 +6157,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1114.002', name: 'Remote Email Collection', - reference: 'https://attack.mitre.org/techniques/T1114/002', + reference: 'https://attack.mitre.org/techniques/T1114/002/', tactics: ['collection'], techniqueId: 'T1114', value: 'remoteEmailCollection', @@ -6169,7 +6169,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.003', name: 'Rename System Utilities', - reference: 'https://attack.mitre.org/techniques/T1036/003', + reference: 'https://attack.mitre.org/techniques/T1036/003/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'renameSystemUtilities', @@ -6181,7 +6181,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.009', name: 'Resource Forking', - reference: 'https://attack.mitre.org/techniques/T1564/009', + reference: 'https://attack.mitre.org/techniques/T1564/009/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'resourceForking', @@ -6193,7 +6193,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1556.005', name: 'Reversible Encryption', - reference: 'https://attack.mitre.org/techniques/T1556/005', + reference: 'https://attack.mitre.org/techniques/T1556/005/', tactics: ['credential-access', 'defense-evasion', 'persistence'], techniqueId: 'T1556', value: 'reversibleEncryption', @@ -6205,7 +6205,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1578.004', name: 'Revert Cloud Instance', - reference: 'https://attack.mitre.org/techniques/T1578/004', + reference: 'https://attack.mitre.org/techniques/T1578/004/', tactics: ['defense-evasion'], techniqueId: 'T1578', value: 'revertCloudInstance', @@ -6217,7 +6217,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.002', name: 'Right-to-Left Override', - reference: 'https://attack.mitre.org/techniques/T1036/002', + reference: 'https://attack.mitre.org/techniques/T1036/002/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'rightToLeftOverride', @@ -6229,7 +6229,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.006', name: 'Run Virtual Instance', - reference: 'https://attack.mitre.org/techniques/T1564/006', + reference: 'https://attack.mitre.org/techniques/T1564/006/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'runVirtualInstance', @@ -6241,7 +6241,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.011', name: 'Rundll32', - reference: 'https://attack.mitre.org/techniques/T1218/011', + reference: 'https://attack.mitre.org/techniques/T1218/011/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'rundll32', @@ -6253,7 +6253,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1565.003', name: 'Runtime Data Manipulation', - reference: 'https://attack.mitre.org/techniques/T1565/003', + reference: 'https://attack.mitre.org/techniques/T1565/003/', tactics: ['impact'], techniqueId: 'T1565', value: 'runtimeDataManipulation', @@ -6265,7 +6265,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1606.002', name: 'SAML Tokens', - reference: 'https://attack.mitre.org/techniques/T1606/002', + reference: 'https://attack.mitre.org/techniques/T1606/002/', tactics: ['credential-access'], techniqueId: 'T1606', value: 'samlTokens', @@ -6277,7 +6277,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1608.006', name: 'SEO Poisoning', - reference: 'https://attack.mitre.org/techniques/T1608/006', + reference: 'https://attack.mitre.org/techniques/T1608/006/', tactics: ['resource-development'], techniqueId: 'T1608', value: 'seoPoisoning', @@ -6289,7 +6289,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1134.005', name: 'SID-History Injection', - reference: 'https://attack.mitre.org/techniques/T1134/005', + reference: 'https://attack.mitre.org/techniques/T1134/005/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1134', value: 'sidHistoryInjection', @@ -6301,7 +6301,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1553.003', name: 'SIP and Trust Provider Hijacking', - reference: 'https://attack.mitre.org/techniques/T1553/003', + reference: 'https://attack.mitre.org/techniques/T1553/003/', tactics: ['defense-evasion'], techniqueId: 'T1553', value: 'sipAndTrustProviderHijacking', @@ -6313,7 +6313,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.002', name: 'SMB/Windows Admin Shares', - reference: 'https://attack.mitre.org/techniques/T1021/002', + reference: 'https://attack.mitre.org/techniques/T1021/002/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'smbWindowsAdminShares', @@ -6325,7 +6325,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1602.001', name: 'SNMP (MIB Dump)', - reference: 'https://attack.mitre.org/techniques/T1602/001', + reference: 'https://attack.mitre.org/techniques/T1602/001/', tactics: ['collection'], techniqueId: 'T1602', value: 'snmpMibDump', @@ -6337,7 +6337,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1505.001', name: 'SQL Stored Procedures', - reference: 'https://attack.mitre.org/techniques/T1505/001', + reference: 'https://attack.mitre.org/techniques/T1505/001/', tactics: ['persistence'], techniqueId: 'T1505', value: 'sqlStoredProcedures', @@ -6349,7 +6349,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.004', name: 'SSH', - reference: 'https://attack.mitre.org/techniques/T1021/004', + reference: 'https://attack.mitre.org/techniques/T1021/004/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'ssh', @@ -6361,7 +6361,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1098.004', name: 'SSH Authorized Keys', - reference: 'https://attack.mitre.org/techniques/T1098/004', + reference: 'https://attack.mitre.org/techniques/T1098/004/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1098', value: 'sshAuthorizedKeys', @@ -6373,7 +6373,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1563.001', name: 'SSH Hijacking', - reference: 'https://attack.mitre.org/techniques/T1563/001', + reference: 'https://attack.mitre.org/techniques/T1563/001/', tactics: ['lateral-movement'], techniqueId: 'T1563', value: 'sshHijacking', @@ -6385,7 +6385,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.009', name: 'Safe Mode Boot', - reference: 'https://attack.mitre.org/techniques/T1562/009', + reference: 'https://attack.mitre.org/techniques/T1562/009/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'safeModeBoot', @@ -6397,7 +6397,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1596.005', name: 'Scan Databases', - reference: 'https://attack.mitre.org/techniques/T1596/005', + reference: 'https://attack.mitre.org/techniques/T1596/005/', tactics: ['reconnaissance'], techniqueId: 'T1596', value: 'scanDatabases', @@ -6409,7 +6409,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1595.001', name: 'Scanning IP Blocks', - reference: 'https://attack.mitre.org/techniques/T1595/001', + reference: 'https://attack.mitre.org/techniques/T1595/001/', tactics: ['reconnaissance'], techniqueId: 'T1595', value: 'scanningIpBlocks', @@ -6421,7 +6421,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1053.005', name: 'Scheduled Task', - reference: 'https://attack.mitre.org/techniques/T1053/005', + reference: 'https://attack.mitre.org/techniques/T1053/005/', tactics: ['execution', 'persistence', 'privilege-escalation'], techniqueId: 'T1053', value: 'scheduledTask', @@ -6433,7 +6433,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.002', name: 'Screensaver', - reference: 'https://attack.mitre.org/techniques/T1546/002', + reference: 'https://attack.mitre.org/techniques/T1546/002/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'screensaver', @@ -6445,7 +6445,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1593.002', name: 'Search Engines', - reference: 'https://attack.mitre.org/techniques/T1593/002', + reference: 'https://attack.mitre.org/techniques/T1593/002/', tactics: ['reconnaissance'], techniqueId: 'T1593', value: 'searchEngines', @@ -6457,7 +6457,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1003.002', name: 'Security Account Manager', - reference: 'https://attack.mitre.org/techniques/T1003/002', + reference: 'https://attack.mitre.org/techniques/T1003/002/', tactics: ['credential-access'], techniqueId: 'T1003', value: 'securityAccountManager', @@ -6469,7 +6469,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1518.001', name: 'Security Software Discovery', - reference: 'https://attack.mitre.org/techniques/T1518/001', + reference: 'https://attack.mitre.org/techniques/T1518/001/', tactics: ['discovery'], techniqueId: 'T1518', value: 'securitySoftwareDiscovery', @@ -6481,7 +6481,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.005', name: 'Security Support Provider', - reference: 'https://attack.mitre.org/techniques/T1547/005', + reference: 'https://attack.mitre.org/techniques/T1547/005/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'securitySupportProvider', @@ -6493,7 +6493,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1555.002', name: 'Securityd Memory', - reference: 'https://attack.mitre.org/techniques/T1555/002', + reference: 'https://attack.mitre.org/techniques/T1555/002/', tactics: ['credential-access'], techniqueId: 'T1555', value: 'securitydMemory', @@ -6505,7 +6505,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.004', name: 'Server', - reference: 'https://attack.mitre.org/techniques/T1583/004', + reference: 'https://attack.mitre.org/techniques/T1583/004/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'server', @@ -6517,7 +6517,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.004', name: 'Server', - reference: 'https://attack.mitre.org/techniques/T1584/004', + reference: 'https://attack.mitre.org/techniques/T1584/004/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'server', @@ -6529,7 +6529,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.007', name: 'Serverless', - reference: 'https://attack.mitre.org/techniques/T1583/007', + reference: 'https://attack.mitre.org/techniques/T1583/007/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'serverless', @@ -6541,7 +6541,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.007', name: 'Serverless', - reference: 'https://attack.mitre.org/techniques/T1584/007', + reference: 'https://attack.mitre.org/techniques/T1584/007/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'serverless', @@ -6553,7 +6553,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1569.002', name: 'Service Execution', - reference: 'https://attack.mitre.org/techniques/T1569/002', + reference: 'https://attack.mitre.org/techniques/T1569/002/', tactics: ['execution'], techniqueId: 'T1569', value: 'serviceExecution', @@ -6565,7 +6565,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1499.002', name: 'Service Exhaustion Flood', - reference: 'https://attack.mitre.org/techniques/T1499/002', + reference: 'https://attack.mitre.org/techniques/T1499/002/', tactics: ['impact'], techniqueId: 'T1499', value: 'serviceExhaustionFlood', @@ -6577,7 +6577,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.010', name: 'Services File Permissions Weakness', - reference: 'https://attack.mitre.org/techniques/T1574/010', + reference: 'https://attack.mitre.org/techniques/T1574/010/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'servicesFilePermissionsWeakness', @@ -6589,7 +6589,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1574.011', name: 'Services Registry Permissions Weakness', - reference: 'https://attack.mitre.org/techniques/T1574/011', + reference: 'https://attack.mitre.org/techniques/T1574/011/', tactics: ['persistence', 'privilege-escalation', 'defense-evasion'], techniqueId: 'T1574', value: 'servicesRegistryPermissionsWeakness', @@ -6601,7 +6601,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1548.001', name: 'Setuid and Setgid', - reference: 'https://attack.mitre.org/techniques/T1548/001', + reference: 'https://attack.mitre.org/techniques/T1548/001/', tactics: ['privilege-escalation', 'defense-evasion'], techniqueId: 'T1548', value: 'setuidAndSetgid', @@ -6613,7 +6613,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1213.002', name: 'Sharepoint', - reference: 'https://attack.mitre.org/techniques/T1213/002', + reference: 'https://attack.mitre.org/techniques/T1213/002/', tactics: ['collection'], techniqueId: 'T1213', value: 'sharepoint', @@ -6625,7 +6625,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.009', name: 'Shortcut Modification', - reference: 'https://attack.mitre.org/techniques/T1547/009', + reference: 'https://attack.mitre.org/techniques/T1547/009/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'shortcutModification', @@ -6637,7 +6637,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1558.002', name: 'Silver Ticket', - reference: 'https://attack.mitre.org/techniques/T1558/002', + reference: 'https://attack.mitre.org/techniques/T1558/002/', tactics: ['credential-access'], techniqueId: 'T1558', value: 'silverTicket', @@ -6649,7 +6649,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1593.001', name: 'Social Media', - reference: 'https://attack.mitre.org/techniques/T1593/001', + reference: 'https://attack.mitre.org/techniques/T1593/001/', tactics: ['reconnaissance'], techniqueId: 'T1593', value: 'socialMedia', @@ -6661,7 +6661,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1586.001', name: 'Social Media Accounts', - reference: 'https://attack.mitre.org/techniques/T1586/001', + reference: 'https://attack.mitre.org/techniques/T1586/001/', tactics: ['resource-development'], techniqueId: 'T1586', value: 'socialMediaAccounts', @@ -6673,7 +6673,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1585.001', name: 'Social Media Accounts', - reference: 'https://attack.mitre.org/techniques/T1585/001', + reference: 'https://attack.mitre.org/techniques/T1585/001/', tactics: ['resource-development'], techniqueId: 'T1585', value: 'socialMediaAccounts', @@ -6685,7 +6685,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1205.002', name: 'Socket Filters', - reference: 'https://attack.mitre.org/techniques/T1205/002', + reference: 'https://attack.mitre.org/techniques/T1205/002/', tactics: ['defense-evasion', 'persistence', 'command-and-control'], techniqueId: 'T1205', value: 'socketFilters', @@ -6697,7 +6697,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1592.002', name: 'Software', - reference: 'https://attack.mitre.org/techniques/T1592/002', + reference: 'https://attack.mitre.org/techniques/T1592/002/', tactics: ['reconnaissance'], techniqueId: 'T1592', value: 'software', @@ -6709,7 +6709,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.002', name: 'Software Packing', - reference: 'https://attack.mitre.org/techniques/T1027/002', + reference: 'https://attack.mitre.org/techniques/T1027/002/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'softwarePacking', @@ -6721,7 +6721,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1036.006', name: 'Space after Filename', - reference: 'https://attack.mitre.org/techniques/T1036/006', + reference: 'https://attack.mitre.org/techniques/T1036/006/', tactics: ['defense-evasion'], techniqueId: 'T1036', value: 'spaceAfterFilename', @@ -6733,7 +6733,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1566.001', name: 'Spearphishing Attachment', - reference: 'https://attack.mitre.org/techniques/T1566/001', + reference: 'https://attack.mitre.org/techniques/T1566/001/', tactics: ['initial-access'], techniqueId: 'T1566', value: 'spearphishingAttachment', @@ -6745,7 +6745,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1598.002', name: 'Spearphishing Attachment', - reference: 'https://attack.mitre.org/techniques/T1598/002', + reference: 'https://attack.mitre.org/techniques/T1598/002/', tactics: ['reconnaissance'], techniqueId: 'T1598', value: 'spearphishingAttachment', @@ -6757,7 +6757,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1566.002', name: 'Spearphishing Link', - reference: 'https://attack.mitre.org/techniques/T1566/002', + reference: 'https://attack.mitre.org/techniques/T1566/002/', tactics: ['initial-access'], techniqueId: 'T1566', value: 'spearphishingLink', @@ -6769,7 +6769,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1598.003', name: 'Spearphishing Link', - reference: 'https://attack.mitre.org/techniques/T1598/003', + reference: 'https://attack.mitre.org/techniques/T1598/003/', tactics: ['reconnaissance'], techniqueId: 'T1598', value: 'spearphishingLink', @@ -6781,7 +6781,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1598.001', name: 'Spearphishing Service', - reference: 'https://attack.mitre.org/techniques/T1598/001', + reference: 'https://attack.mitre.org/techniques/T1598/001/', tactics: ['reconnaissance'], techniqueId: 'T1598', value: 'spearphishingService', @@ -6793,7 +6793,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1598.004', name: 'Spearphishing Voice', - reference: 'https://attack.mitre.org/techniques/T1598/004', + reference: 'https://attack.mitre.org/techniques/T1598/004/', tactics: ['reconnaissance'], techniqueId: 'T1598', value: 'spearphishingVoice', @@ -6805,7 +6805,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1566.004', name: 'Spearphishing Voice', - reference: 'https://attack.mitre.org/techniques/T1566/004', + reference: 'https://attack.mitre.org/techniques/T1566/004/', tactics: ['initial-access'], techniqueId: 'T1566', value: 'spearphishingVoice', @@ -6817,7 +6817,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1566.003', name: 'Spearphishing via Service', - reference: 'https://attack.mitre.org/techniques/T1566/003', + reference: 'https://attack.mitre.org/techniques/T1566/003/', tactics: ['initial-access'], techniqueId: 'T1566', value: 'spearphishingViaService', @@ -6829,7 +6829,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1562.011', name: 'Spoof Security Alerting', - reference: 'https://attack.mitre.org/techniques/T1562/011', + reference: 'https://attack.mitre.org/techniques/T1562/011/', tactics: ['defense-evasion'], techniqueId: 'T1562', value: 'spoofSecurityAlerting', @@ -6841,7 +6841,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1132.001', name: 'Standard Encoding', - reference: 'https://attack.mitre.org/techniques/T1132/001', + reference: 'https://attack.mitre.org/techniques/T1132/001/', tactics: ['command-and-control'], techniqueId: 'T1132', value: 'standardEncoding', @@ -6853,7 +6853,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1037.005', name: 'Startup Items', - reference: 'https://attack.mitre.org/techniques/T1037/005', + reference: 'https://attack.mitre.org/techniques/T1037/005/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1037', value: 'startupItems', @@ -6865,7 +6865,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.003', name: 'Steganography', - reference: 'https://attack.mitre.org/techniques/T1027/003', + reference: 'https://attack.mitre.org/techniques/T1027/003/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'steganography', @@ -6877,7 +6877,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1001.002', name: 'Steganography', - reference: 'https://attack.mitre.org/techniques/T1001/002', + reference: 'https://attack.mitre.org/techniques/T1001/002/', tactics: ['command-and-control'], techniqueId: 'T1001', value: 'steganography', @@ -6889,7 +6889,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1565.001', name: 'Stored Data Manipulation', - reference: 'https://attack.mitre.org/techniques/T1565/001', + reference: 'https://attack.mitre.org/techniques/T1565/001/', tactics: ['impact'], techniqueId: 'T1565', value: 'storedDataManipulation', @@ -6901,7 +6901,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1027.008', name: 'Stripped Payloads', - reference: 'https://attack.mitre.org/techniques/T1027/008', + reference: 'https://attack.mitre.org/techniques/T1027/008/', tactics: ['defense-evasion'], techniqueId: 'T1027', value: 'strippedPayloads', @@ -6913,7 +6913,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1548.003', name: 'Sudo and Sudo Caching', - reference: 'https://attack.mitre.org/techniques/T1548/003', + reference: 'https://attack.mitre.org/techniques/T1548/003/', tactics: ['privilege-escalation', 'defense-evasion'], techniqueId: 'T1548', value: 'sudoAndSudoCaching', @@ -6925,7 +6925,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1573.001', name: 'Symmetric Cryptography', - reference: 'https://attack.mitre.org/techniques/T1573/001', + reference: 'https://attack.mitre.org/techniques/T1573/001/', tactics: ['command-and-control'], techniqueId: 'T1573', value: 'symmetricCryptography', @@ -6937,7 +6937,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1216.002', name: 'SyncAppvPublishingServer', - reference: 'https://attack.mitre.org/techniques/T1216/002', + reference: 'https://attack.mitre.org/techniques/T1216/002/', tactics: ['defense-evasion'], techniqueId: 'T1216', value: 'syncAppvPublishingServer', @@ -6949,7 +6949,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1497.001', name: 'System Checks', - reference: 'https://attack.mitre.org/techniques/T1497/001', + reference: 'https://attack.mitre.org/techniques/T1497/001/', tactics: ['defense-evasion', 'discovery'], techniqueId: 'T1497', value: 'systemChecks', @@ -6961,7 +6961,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1542.001', name: 'System Firmware', - reference: 'https://attack.mitre.org/techniques/T1542/001', + reference: 'https://attack.mitre.org/techniques/T1542/001/', tactics: ['persistence', 'defense-evasion'], techniqueId: 'T1542', value: 'systemFirmware', @@ -6973,7 +6973,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1614.001', name: 'System Language Discovery', - reference: 'https://attack.mitre.org/techniques/T1614/001', + reference: 'https://attack.mitre.org/techniques/T1614/001/', tactics: ['discovery'], techniqueId: 'T1614', value: 'systemLanguageDiscovery', @@ -6985,7 +6985,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1543.002', name: 'Systemd Service', - reference: 'https://attack.mitre.org/techniques/T1543/002', + reference: 'https://attack.mitre.org/techniques/T1543/002/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1543', value: 'systemdService', @@ -6997,7 +6997,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1053.006', name: 'Systemd Timers', - reference: 'https://attack.mitre.org/techniques/T1053/006', + reference: 'https://attack.mitre.org/techniques/T1053/006/', tactics: ['execution', 'persistence', 'privilege-escalation'], techniqueId: 'T1053', value: 'systemdTimers', @@ -7009,7 +7009,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1548.006', name: 'TCC Manipulation', - reference: 'https://attack.mitre.org/techniques/T1548/006', + reference: 'https://attack.mitre.org/techniques/T1548/006/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1548', value: 'tccManipulation', @@ -7021,7 +7021,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1542.005', name: 'TFTP Boot', - reference: 'https://attack.mitre.org/techniques/T1542/005', + reference: 'https://attack.mitre.org/techniques/T1542/005/', tactics: ['defense-evasion', 'persistence'], techniqueId: 'T1542', value: 'tftpBoot', @@ -7033,7 +7033,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1548.005', name: 'Temporary Elevated Cloud Access', - reference: 'https://attack.mitre.org/techniques/T1548/005', + reference: 'https://attack.mitre.org/techniques/T1548/005/', tactics: ['privilege-escalation', 'defense-evasion'], techniqueId: 'T1548', value: 'temporaryElevatedCloudAccess', @@ -7045,7 +7045,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1505.005', name: 'Terminal Services DLL', - reference: 'https://attack.mitre.org/techniques/T1505/005', + reference: 'https://attack.mitre.org/techniques/T1505/005/', tactics: ['persistence'], techniqueId: 'T1505', value: 'terminalServicesDll', @@ -7057,7 +7057,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.003', name: 'Thread Execution Hijacking', - reference: 'https://attack.mitre.org/techniques/T1055/003', + reference: 'https://attack.mitre.org/techniques/T1055/003/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'threadExecutionHijacking', @@ -7069,7 +7069,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.005', name: 'Thread Local Storage', - reference: 'https://attack.mitre.org/techniques/T1055/005', + reference: 'https://attack.mitre.org/techniques/T1055/005/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'threadLocalStorage', @@ -7081,7 +7081,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1597.001', name: 'Threat Intel Vendors', - reference: 'https://attack.mitre.org/techniques/T1597/001', + reference: 'https://attack.mitre.org/techniques/T1597/001/', tactics: ['reconnaissance'], techniqueId: 'T1597', value: 'threatIntelVendors', @@ -7093,7 +7093,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1497.003', name: 'Time Based Evasion', - reference: 'https://attack.mitre.org/techniques/T1497/003', + reference: 'https://attack.mitre.org/techniques/T1497/003/', tactics: ['defense-evasion', 'discovery'], techniqueId: 'T1497', value: 'timeBasedEvasion', @@ -7105,7 +7105,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.003', name: 'Time Providers', - reference: 'https://attack.mitre.org/techniques/T1547/003', + reference: 'https://attack.mitre.org/techniques/T1547/003/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'timeProviders', @@ -7117,7 +7117,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1070.006', name: 'Timestomp', - reference: 'https://attack.mitre.org/techniques/T1070/006', + reference: 'https://attack.mitre.org/techniques/T1070/006/', tactics: ['defense-evasion'], techniqueId: 'T1070', value: 'timestomp', @@ -7129,7 +7129,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1134.001', name: 'Token Impersonation/Theft', - reference: 'https://attack.mitre.org/techniques/T1134/001', + reference: 'https://attack.mitre.org/techniques/T1134/001/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1134', value: 'tokenImpersonationTheft', @@ -7141,7 +7141,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.002', name: 'Tool', - reference: 'https://attack.mitre.org/techniques/T1588/002', + reference: 'https://attack.mitre.org/techniques/T1588/002/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'tool', @@ -7153,7 +7153,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1020.001', name: 'Traffic Duplication', - reference: 'https://attack.mitre.org/techniques/T1020/001', + reference: 'https://attack.mitre.org/techniques/T1020/001/', tactics: ['exfiltration'], techniqueId: 'T1020', value: 'trafficDuplication', @@ -7165,7 +7165,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1565.002', name: 'Transmitted Data Manipulation', - reference: 'https://attack.mitre.org/techniques/T1565/002', + reference: 'https://attack.mitre.org/techniques/T1565/002/', tactics: ['impact'], techniqueId: 'T1565', value: 'transmittedDataManipulation', @@ -7177,7 +7177,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1505.002', name: 'Transport Agent', - reference: 'https://attack.mitre.org/techniques/T1505/002', + reference: 'https://attack.mitre.org/techniques/T1505/002/', tactics: ['persistence'], techniqueId: 'T1505', value: 'transportAgent', @@ -7189,7 +7189,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.005', name: 'Trap', - reference: 'https://attack.mitre.org/techniques/T1546/005', + reference: 'https://attack.mitre.org/techniques/T1546/005/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'trap', @@ -7201,7 +7201,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1484.002', name: 'Trust Modification', - reference: 'https://attack.mitre.org/techniques/T1484/002', + reference: 'https://attack.mitre.org/techniques/T1484/002/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1484', value: 'trustModification', @@ -7213,7 +7213,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.004', name: 'Unix Shell', - reference: 'https://attack.mitre.org/techniques/T1059/004', + reference: 'https://attack.mitre.org/techniques/T1059/004/', tactics: ['execution'], techniqueId: 'T1059', value: 'unixShell', @@ -7225,7 +7225,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.004', name: 'Unix Shell Configuration Modification', - reference: 'https://attack.mitre.org/techniques/T1546/004', + reference: 'https://attack.mitre.org/techniques/T1546/004/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'unixShellConfigurationModification', @@ -7237,7 +7237,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1608.001', name: 'Upload Malware', - reference: 'https://attack.mitre.org/techniques/T1608/001', + reference: 'https://attack.mitre.org/techniques/T1608/001/', tactics: ['resource-development'], techniqueId: 'T1608', value: 'uploadMalware', @@ -7249,7 +7249,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1608.002', name: 'Upload Tool', - reference: 'https://attack.mitre.org/techniques/T1608/002', + reference: 'https://attack.mitre.org/techniques/T1608/002/', tactics: ['resource-development'], techniqueId: 'T1608', value: 'uploadTool', @@ -7261,7 +7261,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1497.002', name: 'User Activity Based Checks', - reference: 'https://attack.mitre.org/techniques/T1497/002', + reference: 'https://attack.mitre.org/techniques/T1497/002/', tactics: ['defense-evasion', 'discovery'], techniqueId: 'T1497', value: 'userActivityBasedChecks', @@ -7273,7 +7273,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1564.007', name: 'VBA Stomping', - reference: 'https://attack.mitre.org/techniques/T1564/007', + reference: 'https://attack.mitre.org/techniques/T1564/007/', tactics: ['defense-evasion'], techniqueId: 'T1564', value: 'vbaStomping', @@ -7285,7 +7285,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1055.014', name: 'VDSO Hijacking', - reference: 'https://attack.mitre.org/techniques/T1055/014', + reference: 'https://attack.mitre.org/techniques/T1055/014/', tactics: ['defense-evasion', 'privilege-escalation'], techniqueId: 'T1055', value: 'vdsoHijacking', @@ -7297,7 +7297,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.005', name: 'VNC', - reference: 'https://attack.mitre.org/techniques/T1021/005', + reference: 'https://attack.mitre.org/techniques/T1021/005/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'vnc', @@ -7309,7 +7309,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1218.012', name: 'Verclsid', - reference: 'https://attack.mitre.org/techniques/T1218/012', + reference: 'https://attack.mitre.org/techniques/T1218/012/', tactics: ['defense-evasion'], techniqueId: 'T1218', value: 'verclsid', @@ -7321,7 +7321,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.003', name: 'Virtual Private Server', - reference: 'https://attack.mitre.org/techniques/T1584/003', + reference: 'https://attack.mitre.org/techniques/T1584/003/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'virtualPrivateServer', @@ -7333,7 +7333,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.003', name: 'Virtual Private Server', - reference: 'https://attack.mitre.org/techniques/T1583/003', + reference: 'https://attack.mitre.org/techniques/T1583/003/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'virtualPrivateServer', @@ -7345,7 +7345,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.005', name: 'Visual Basic', - reference: 'https://attack.mitre.org/techniques/T1059/005', + reference: 'https://attack.mitre.org/techniques/T1059/005/', tactics: ['execution'], techniqueId: 'T1059', value: 'visualBasic', @@ -7357,7 +7357,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1588.006', name: 'Vulnerabilities', - reference: 'https://attack.mitre.org/techniques/T1588/006', + reference: 'https://attack.mitre.org/techniques/T1588/006/', tactics: ['resource-development'], techniqueId: 'T1588', value: 'vulnerabilities', @@ -7369,7 +7369,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1595.002', name: 'Vulnerability Scanning', - reference: 'https://attack.mitre.org/techniques/T1595/002', + reference: 'https://attack.mitre.org/techniques/T1595/002/', tactics: ['reconnaissance'], techniqueId: 'T1595', value: 'vulnerabilityScanning', @@ -7381,7 +7381,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1596.002', name: 'WHOIS', - reference: 'https://attack.mitre.org/techniques/T1596/002', + reference: 'https://attack.mitre.org/techniques/T1596/002/', tactics: ['reconnaissance'], techniqueId: 'T1596', value: 'whois', @@ -7393,7 +7393,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1606.001', name: 'Web Cookies', - reference: 'https://attack.mitre.org/techniques/T1606/001', + reference: 'https://attack.mitre.org/techniques/T1606/001/', tactics: ['credential-access'], techniqueId: 'T1606', value: 'webCookies', @@ -7405,7 +7405,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1056.003', name: 'Web Portal Capture', - reference: 'https://attack.mitre.org/techniques/T1056/003', + reference: 'https://attack.mitre.org/techniques/T1056/003/', tactics: ['collection', 'credential-access'], techniqueId: 'T1056', value: 'webPortalCapture', @@ -7417,7 +7417,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1071.001', name: 'Web Protocols', - reference: 'https://attack.mitre.org/techniques/T1071/001', + reference: 'https://attack.mitre.org/techniques/T1071/001/', tactics: ['command-and-control'], techniqueId: 'T1071', value: 'webProtocols', @@ -7429,7 +7429,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1583.006', name: 'Web Services', - reference: 'https://attack.mitre.org/techniques/T1583/006', + reference: 'https://attack.mitre.org/techniques/T1583/006/', tactics: ['resource-development'], techniqueId: 'T1583', value: 'webServices', @@ -7441,7 +7441,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1584.006', name: 'Web Services', - reference: 'https://attack.mitre.org/techniques/T1584/006', + reference: 'https://attack.mitre.org/techniques/T1584/006/', tactics: ['resource-development'], techniqueId: 'T1584', value: 'webServices', @@ -7453,7 +7453,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1550.004', name: 'Web Session Cookie', - reference: 'https://attack.mitre.org/techniques/T1550/004', + reference: 'https://attack.mitre.org/techniques/T1550/004/', tactics: ['defense-evasion', 'lateral-movement'], techniqueId: 'T1550', value: 'webSessionCookie', @@ -7465,7 +7465,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1505.003', name: 'Web Shell', - reference: 'https://attack.mitre.org/techniques/T1505/003', + reference: 'https://attack.mitre.org/techniques/T1505/003/', tactics: ['persistence'], techniqueId: 'T1505', value: 'webShell', @@ -7477,7 +7477,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1016.002', name: 'Wi-Fi Discovery', - reference: 'https://attack.mitre.org/techniques/T1016/002', + reference: 'https://attack.mitre.org/techniques/T1016/002/', tactics: ['discovery'], techniqueId: 'T1016', value: 'wiFiDiscovery', @@ -7489,7 +7489,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1059.003', name: 'Windows Command Shell', - reference: 'https://attack.mitre.org/techniques/T1059/003', + reference: 'https://attack.mitre.org/techniques/T1059/003/', tactics: ['execution'], techniqueId: 'T1059', value: 'windowsCommandShell', @@ -7501,7 +7501,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1555.004', name: 'Windows Credential Manager', - reference: 'https://attack.mitre.org/techniques/T1555/004', + reference: 'https://attack.mitre.org/techniques/T1555/004/', tactics: ['credential-access'], techniqueId: 'T1555', value: 'windowsCredentialManager', @@ -7513,7 +7513,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1222.001', name: 'Windows File and Directory Permissions Modification', - reference: 'https://attack.mitre.org/techniques/T1222/001', + reference: 'https://attack.mitre.org/techniques/T1222/001/', tactics: ['defense-evasion'], techniqueId: 'T1222', value: 'windowsFileAndDirectoryPermissionsModification', @@ -7525,7 +7525,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1546.003', name: 'Windows Management Instrumentation Event Subscription', - reference: 'https://attack.mitre.org/techniques/T1546/003', + reference: 'https://attack.mitre.org/techniques/T1546/003/', tactics: ['privilege-escalation', 'persistence'], techniqueId: 'T1546', value: 'windowsManagementInstrumentationEventSubscription', @@ -7537,7 +7537,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1021.006', name: 'Windows Remote Management', - reference: 'https://attack.mitre.org/techniques/T1021/006', + reference: 'https://attack.mitre.org/techniques/T1021/006/', tactics: ['lateral-movement'], techniqueId: 'T1021', value: 'windowsRemoteManagement', @@ -7549,7 +7549,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1543.003', name: 'Windows Service', - reference: 'https://attack.mitre.org/techniques/T1543/003', + reference: 'https://attack.mitre.org/techniques/T1543/003/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1543', value: 'windowsService', @@ -7561,7 +7561,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.004', name: 'Winlogon Helper DLL', - reference: 'https://attack.mitre.org/techniques/T1547/004', + reference: 'https://attack.mitre.org/techniques/T1547/004/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'winlogonHelperDll', @@ -7573,7 +7573,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1595.003', name: 'Wordlist Scanning', - reference: 'https://attack.mitre.org/techniques/T1595/003', + reference: 'https://attack.mitre.org/techniques/T1595/003/', tactics: ['reconnaissance'], techniqueId: 'T1595', value: 'wordlistScanning', @@ -7585,7 +7585,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1547.013', name: 'XDG Autostart Entries', - reference: 'https://attack.mitre.org/techniques/T1547/013', + reference: 'https://attack.mitre.org/techniques/T1547/013/', tactics: ['persistence', 'privilege-escalation'], techniqueId: 'T1547', value: 'xdgAutostartEntries', @@ -7597,7 +7597,7 @@ export const subtechniques: MitreSubTechnique[] = [ ), id: 'T1559.003', name: 'XPC Services', - reference: 'https://attack.mitre.org/techniques/T1559/003', + reference: 'https://attack.mitre.org/techniques/T1559/003/', tactics: ['execution'], techniqueId: 'T1559', value: 'xpcServices', @@ -7614,18 +7614,18 @@ export const getMockThreatData = () => [ tactic: { name: 'Credential Access', id: 'TA0006', - reference: 'https://attack.mitre.org/tactics/TA0006', + reference: 'https://attack.mitre.org/tactics/TA0006/', }, technique: { name: 'OS Credential Dumping', id: 'T1003', - reference: 'https://attack.mitre.org/techniques/T1003', + reference: 'https://attack.mitre.org/techniques/T1003/', tactics: ['credential-access'], }, subtechnique: { name: '/etc/passwd and /etc/shadow', id: 'T1003.008', - reference: 'https://attack.mitre.org/techniques/T1003/008', + reference: 'https://attack.mitre.org/techniques/T1003/008/', tactics: ['credential-access'], techniqueId: 'T1003', }, @@ -7634,18 +7634,18 @@ export const getMockThreatData = () => [ tactic: { name: 'Discovery', id: 'TA0007', - reference: 'https://attack.mitre.org/tactics/TA0007', + reference: 'https://attack.mitre.org/tactics/TA0007/', }, technique: { name: 'Account Discovery', id: 'T1087', - reference: 'https://attack.mitre.org/techniques/T1087', + reference: 'https://attack.mitre.org/techniques/T1087/', tactics: ['discovery'], }, subtechnique: { name: 'Cloud Account', id: 'T1087.004', - reference: 'https://attack.mitre.org/techniques/T1087/004', + reference: 'https://attack.mitre.org/techniques/T1087/004/', tactics: ['discovery'], techniqueId: 'T1087', }, @@ -7654,18 +7654,18 @@ export const getMockThreatData = () => [ tactic: { name: 'Command and Control', id: 'TA0011', - reference: 'https://attack.mitre.org/tactics/TA0011', + reference: 'https://attack.mitre.org/tactics/TA0011/', }, technique: { name: 'Web Service', id: 'T1102', - reference: 'https://attack.mitre.org/techniques/T1102', + reference: 'https://attack.mitre.org/techniques/T1102/', tactics: ['command-and-control'], }, subtechnique: { name: 'Dead Drop Resolver', id: 'T1102.001', - reference: 'https://attack.mitre.org/techniques/T1102/001', + reference: 'https://attack.mitre.org/techniques/T1102/001/', tactics: ['command-and-control'], techniqueId: 'T1102', }, @@ -7674,18 +7674,18 @@ export const getMockThreatData = () => [ tactic: { name: 'Defense Evasion', id: 'TA0005', - reference: 'https://attack.mitre.org/tactics/TA0005', + reference: 'https://attack.mitre.org/tactics/TA0005/', }, technique: { name: 'Obfuscated Files or Information', id: 'T1027', - reference: 'https://attack.mitre.org/techniques/T1027', + reference: 'https://attack.mitre.org/techniques/T1027/', tactics: ['defense-evasion'], }, subtechnique: { name: 'Encrypted/Encoded File', id: 'T1027.013', - reference: 'https://attack.mitre.org/techniques/T1027/013', + reference: 'https://attack.mitre.org/techniques/T1027/013/', tactics: ['defense-evasion'], techniqueId: 'T1027', }, @@ -7702,12 +7702,12 @@ export const getDuplicateTechniqueThreatData = () => [ tactic: { name: 'Privilege Escalation', id: 'TA0004', - reference: 'https://attack.mitre.org/tactics/TA0004', + reference: 'https://attack.mitre.org/tactics/TA0004/', }, technique: { name: 'Event Triggered Execution', id: 'T1546', - reference: 'https://attack.mitre.org/techniques/T1546', + reference: 'https://attack.mitre.org/techniques/T1546/', tactics: ['privilege-escalation', 'persistence'], }, }, @@ -7715,12 +7715,12 @@ export const getDuplicateTechniqueThreatData = () => [ tactic: { name: 'Persistence', id: 'TA0003', - reference: 'https://attack.mitre.org/tactics/TA0003', + reference: 'https://attack.mitre.org/tactics/TA0003/', }, technique: { name: 'Event Triggered Execution', id: 'T1546', - reference: 'https://attack.mitre.org/techniques/T1546', + reference: 'https://attack.mitre.org/techniques/T1546/', tactics: ['privilege-escalation', 'persistence'], }, }, diff --git a/x-pack/solutions/security/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx index c4d1f90be09d2..a4f87272c80ac 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/exceptions/components/exceptions_list_card/index.tsx @@ -18,12 +18,11 @@ import { useEuiTheme, useEuiShadow, } from '@elastic/eui'; -import { css } from '@emotion/css'; +import { css } from '@emotion/react'; +import styled from '@emotion/styled'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; import { HeaderMenu } from '@kbn/securitysolution-exception-list-components'; -import styled from 'styled-components'; -import { euiThemeVars } from '@kbn/ui-theme'; import type { Rule } from '../../../detection_engine/rule_management/logic/types'; import { EditExceptionFlyout } from '../../../detection_engine/rule_exceptions/components/edit_exception_flyout'; import { AddExceptionFlyout } from '../../../detection_engine/rule_exceptions/components/add_exception_flyout'; @@ -75,12 +74,17 @@ interface ExceptionsListCardProps { } const ExceptionPanel = styled(EuiPanel)` - margin: -${euiThemeVars.euiSizeS} ${euiThemeVars.euiSizeM} 0 ${euiThemeVars.euiSizeM}; + margin: ${({ + theme: { + euiTheme: { size }, + }, + }) => `-${size.s} ${size.m} 0 ${size.m}`}; `; const ListHeaderContainer = styled(EuiFlexGroup)` - padding: ${euiThemeVars.euiSizeS}; + padding: ${({ theme }) => theme.euiTheme.size.s}; text-align: initial; `; + export const ExceptionsListCard = memo( ({ exceptionsList, handleDelete, handleExport, handleDuplicate, readOnly }) => { const { @@ -156,10 +160,7 @@ export const ExceptionsListCard = memo( return ( <> setToggleAccordion(!toggleAccordion)} diff --git a/x-pack/solutions/security/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx index 423668347d4fc..873025bcd3baf 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/exceptions/pages/shared_lists/index.tsx @@ -22,14 +22,13 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; +import styled from '@emotion/styled'; import type { ExceptionListFilter, NamespaceType } from '@kbn/securitysolution-io-ts-list-types'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { useApi, useExceptionLists } from '@kbn/securitysolution-list-hooks'; import { EmptyViewerState, ViewerStatus } from '@kbn/securitysolution-exception-list-components'; -import styled from 'styled-components'; -import { euiThemeVars } from '@kbn/ui-theme'; import { AutoDownload } from '../../../common/components/auto_download/auto_download'; import { useKibana } from '../../../common/lib/kibana'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; @@ -82,7 +81,7 @@ const SORT_FIELDS: Array<{ field: string; label: string; defaultOrder: 'asc' | ' ]; const ExceptionsTable = styled(EuiFlexGroup)` - padding: ${euiThemeVars.euiSizeL} 0; + padding: ${({ theme }) => theme.euiTheme.size.l} 0; `; export const SharedLists = React.memo(() => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts b/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts index 57eaf5ca1debf..f081d196c2c9c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts @@ -425,6 +425,17 @@ export const AdvancedPolicySchema: AdvancedPolicySchemaType[] = [ } ), }, + { + key: 'mac.advanced.malware.max_file_size_bytes', + first_supported_version: '8.16.4', + documentation: i18n.translate( + 'xpack.securitySolution.endpoint.policy.advanced.mac.advanced.malware.max_file_size_bytes', + { + defaultMessage: + 'The maximum file size in bytes that should be used for evaluating malware. Default: 78643200.', + } + ), + }, { key: 'mac.advanced.kernel.connect', first_supported_version: '7.9', @@ -671,6 +682,17 @@ export const AdvancedPolicySchema: AdvancedPolicySchemaType[] = [ } ), }, + { + key: 'windows.advanced.malware.max_file_size_bytes', + first_supported_version: '8.16.4', + documentation: i18n.translate( + 'xpack.securitySolution.endpoint.policy.advanced.windows.advanced.malware.max_file_size_bytes', + { + defaultMessage: + 'The maximum file size in bytes that should be used for evaluating malware. Default: 78643200.', + } + ), + }, { key: 'windows.advanced.kernel.connect', first_supported_version: '7.9', @@ -919,6 +941,17 @@ export const AdvancedPolicySchema: AdvancedPolicySchemaType[] = [ } ), }, + { + key: 'linux.advanced.malware.max_file_size_bytes', + first_supported_version: '8.16.4', + documentation: i18n.translate( + 'xpack.securitySolution.endpoint.policy.advanced.linux.advanced.malware.max_file_size_bytes', + { + defaultMessage: + 'The maximum file size in bytes that should be used for evaluating malware. Default: 78643200.', + } + ), + }, { key: 'linux.advanced.memory_protection.enable_fork_scan', first_supported_version: '8.14', diff --git a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.test.tsx index 4f346d68739a6..0bc0d6ed4b1e4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/timelines/components/timeline/tabs/session/use_session_view.test.tsx @@ -14,8 +14,8 @@ import { mockTimelineModel, TestProviders } from '../../../../../common/mock'; import { useKibana } from '../../../../../common/lib/kibana'; import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; import { - useTimelineFullScreen, useGlobalFullScreen, + useTimelineFullScreen, } from '../../../../../common/containers/use_full_screen'; import { useSessionView, useSessionViewNavigation } from './use_session_view'; import { TableId } from '@kbn/securitysolution-data-table'; @@ -61,7 +61,6 @@ jest.mock('../../../../../common/lib/kibana', () => { }, timelines: { getLastUpdated: jest.fn(), - getLoadingPanel: jest.fn(), }, }, }), diff --git a/x-pack/solutions/security/plugins/security_solution/public/value_list/components/info.tsx b/x-pack/solutions/security/plugins/security_solution/public/value_list/components/info.tsx index 328c1f0ed1931..42154e200dbc7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/value_list/components/info.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/value_list/components/info.tsx @@ -6,19 +6,19 @@ */ import React from 'react'; import { EuiText } from '@elastic/eui'; -import { css } from '@emotion/css'; -import { euiThemeVars } from '@kbn/ui-theme'; +import styled from '@emotion/styled'; -const info = css` - margin-right: ${euiThemeVars.euiSizeS}; +const InfoContainer = styled(EuiText)` + margin-right: ${({ theme }) => theme.euiTheme.size.s}; `; -const infoLabel = css` - margin-right: ${euiThemeVars.euiSizeXS}; +const Label = styled.span` + font-weight: ${({ theme }) => theme.euiTheme.font.weight.bold}; + margin-right: ${({ theme }) => theme.euiTheme.size.xs}; `; export const Info = ({ label, value }: { value: React.ReactNode; label: string }) => ( - - {label} {value} - + + {value} + ); diff --git a/x-pack/solutions/security/plugins/security_solution/public/value_list/components/value_list_modal.tsx b/x-pack/solutions/security/plugins/security_solution/public/value_list/components/value_list_modal.tsx index f2741d80bd35a..3e242fc6d3ea4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/value_list/components/value_list_modal.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/value_list/components/value_list_modal.tsx @@ -6,6 +6,7 @@ */ import React, { useState, useCallback } from 'react'; import { css } from '@emotion/css'; +import styled from '@emotion/styled'; import type { EuiSearchBarProps } from '@elastic/eui'; import { EuiModal, @@ -19,7 +20,6 @@ import { EuiSearchBar, } from '@elastic/eui'; import { useFindListItems, useGetListById } from '@kbn/securitysolution-list-hooks'; -import { euiThemeVars } from '@kbn/ui-theme'; import { FormattedDate } from '../../common/components/formatted_date'; import { useKibana } from '../../common/lib/kibana'; import { AddListItemPopover } from './add_list_item_popover'; @@ -35,9 +35,9 @@ import { getInfoTotalItems, } from '../translations'; -const modalBodyStyle = css` +const ModalBody = styled(EuiFlexGroup)` overflow: hidden; - padding: ${euiThemeVars.euiSize}; + padding: ${({ theme }) => theme.euiTheme.size.base}; `; const modalWindow = css` @@ -148,7 +148,7 @@ export const ValueListModal = ({ listId, onCloseModal, canWriteIndex }: ValueLis )} - + )} - + ); diff --git a/x-pack/solutions/security/plugins/security_solution/scripts/extract_tactics_techniques_mitre.js b/x-pack/solutions/security/plugins/security_solution/scripts/extract_tactics_techniques_mitre.js index 9ce8fbb72cff6..385b9e4c94a33 100644 --- a/x-pack/solutions/security/plugins/security_solution/scripts/extract_tactics_techniques_mitre.js +++ b/x-pack/solutions/security/plugins/security_solution/scripts/extract_tactics_techniques_mitre.js @@ -80,12 +80,28 @@ const getSubtechniquesOptions = (subtechniques) => }`.replace(/(\r\n|\n|\r)/gm, ' ') ); +const normalizeThreatReference = (reference) => { + try { + const parsed = new URL(reference); + + if (!parsed.pathname.endsWith('/')) { + // Adds a trailing backslash in urls if it doesn't exist to account for + // any inconsistencies between our script generated data and prebuilt rules packages + parsed.pathname = `${parsed.pathname}/`; + } + + return parsed.toString(); + } catch { + return reference; + } +}; + const getIdReference = (references) => { const ref = references.find((r) => r.source_name === 'mitre-attack'); if (ref != null) { return { id: ref.external_id, - reference: ref.url, + reference: normalizeThreatReference(ref.url), }; } else { return { id: '', reference: '' }; diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts index 946447e2815e0..61ddceab607cf 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts @@ -13,7 +13,7 @@ import { ALERT_COUNTS_TOOL } from './alert_counts_tool'; import type { RetrievalQAChain } from 'langchain/chains'; import type { ExecuteConnectorRequestBody } from '@kbn/elastic-assistant-common/impl/schemas/actions_connector/post_actions_connector_execute_route.gen'; import type { ContentReferencesStore } from '@kbn/elastic-assistant-common'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; describe('AlertCountsTool', () => { const alertsIndexPattern = 'alerts-index'; @@ -32,7 +32,7 @@ describe('AlertCountsTool', () => { const isEnabledKnowledgeBase = true; const chain = {} as unknown as RetrievalQAChain; const logger = loggerMock.create(); - const contentReferencesStore = contentReferencesStoreFactoryMock(); + const contentReferencesStore = newContentReferencesStoreMock(); const rest = { isEnabledKnowledgeBase, chain, @@ -191,7 +191,7 @@ describe('AlertCountsTool', () => { replacements, request, ...rest, - contentReferencesStore: false, + contentReferencesStore: undefined, }) as DynamicTool; const result = await tool.func(''); diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts index 0201eeea1578d..e1150948fde1e 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts @@ -23,6 +23,9 @@ export const ALERT_COUNTS_TOOL_DESCRIPTION = export const ALERT_COUNTS_TOOL: AssistantTool = { id: 'alert-counts-tool', name: 'AlertCountsTool', + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: ALERT_COUNTS_TOOL_DESCRIPTION, sourceRegister: APP_UI_ID, isSupported: (params: AssistantToolParams): params is AlertCountsToolParams => { @@ -35,7 +38,7 @@ export const ALERT_COUNTS_TOOL: AssistantTool = { params as AlertCountsToolParams; return new DynamicStructuredTool({ name: 'AlertCountsTool', - description: ALERT_COUNTS_TOOL_DESCRIPTION, + description: params.description || ALERT_COUNTS_TOOL_DESCRIPTION, schema: z.object({}), func: async () => { const query = getAlertsCountQuery(alertsIndexPattern); diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/defend_insights/index.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/defend_insights/index.ts index d09e8d8f8c8a7..0851642388550 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/defend_insights/index.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/defend_insights/index.ts @@ -41,6 +41,9 @@ export interface DefendInsightsToolParams extends AssistantToolParams { export const DEFEND_INSIGHTS_TOOL: AssistantTool = Object.freeze({ id: DEFEND_INSIGHTS_TOOL_ID, name: 'defendInsightsTool', + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: DEFEND_INSIGHTS_TOOL_DESCRIPTION, sourceRegister: APP_UI_ID, @@ -67,7 +70,7 @@ export const DEFEND_INSIGHTS_TOOL: AssistantTool = Object.freeze({ return new DynamicTool({ name: 'DefendInsightsTool', - description: DEFEND_INSIGHTS_TOOL_DESCRIPTION, + description: params.description || DEFEND_INSIGHTS_TOOL_DESCRIPTION, func: async () => { if (llm == null) { throw new Error('LLM is required for Defend Insights'); diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts index 20affb2abf664..0d2af41232f70 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/esql/nl_to_esql_tool.ts @@ -21,6 +21,9 @@ const TOOL_NAME = 'NaturalLanguageESQLTool'; const toolDetails = { id: 'nl-to-esql-tool', name: TOOL_NAME, + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: `You MUST use the "${TOOL_NAME}" function when the user wants to: - breakdown or filter ES|QL queries that are displayed on the current page - convert queries from another language to ES|QL @@ -57,7 +60,8 @@ export const NL_TO_ESQL_TOOL: AssistantTool = { return new DynamicStructuredTool({ name: toolDetails.name, description: - toolDetails.description + (isOssModel ? getPromptSuffixForOssModel(TOOL_NAME) : ''), + (params.description || toolDetails.description) + + (isOssModel ? getPromptSuffixForOssModel(TOOL_NAME) : ''), schema: z.object({ question: z.string().describe(`The user's exact question about ESQL`), }), diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.test.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.test.ts index 4a6683624b980..6218c4c441a44 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.test.ts @@ -12,13 +12,13 @@ import type { ContentReferencesStore, KnowledgeBaseEntryContentReference, } from '@kbn/elastic-assistant-common'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; import { loggerMock } from '@kbn/logging-mocks'; import { Document } from 'langchain/document'; describe('KnowledgeBaseRetievalTool', () => { const logger = loggerMock.create(); - const contentReferencesStore = contentReferencesStoreFactoryMock(); + const contentReferencesStore = newContentReferencesStoreMock(); const getKnowledgeBaseDocumentEntries = jest.fn(); const kbDataClient = { getKnowledgeBaseDocumentEntries }; const defaultArgs = { @@ -68,7 +68,7 @@ describe('KnowledgeBaseRetievalTool', () => { it('does not include citations if contentReferenceStore is false', async () => { const tool = KNOWLEDGE_BASE_RETRIEVAL_TOOL.getTool({ ...defaultArgs, - contentReferencesStore: false, + contentReferencesStore: undefined, }) as DynamicStructuredTool; getKnowledgeBaseDocumentEntries.mockResolvedValue([ diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts index 6bb7455d332ca..689fb7f189ce8 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_retrieval_tool.ts @@ -19,6 +19,9 @@ export interface KnowledgeBaseRetrievalToolParams extends AssistantToolParams { } const toolDetails = { + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: "Call this for fetching details from the user's knowledge base. The knowledge base contains useful information the user wants to store between conversation contexts. Call this function when the user asks for information about themself, like 'what is my favorite...' or 'using my saved....'. Input must always be the free-text query on a single line, with no other text. You are welcome to re-write the query to be a summary of items/things to search for in the knowledge base, as a vector search will be performed to return similar results when requested. If the results returned do not look relevant, disregard and tell the user you were unable to find the information they were looking for. All requests include a `knowledge history` section which includes some existing knowledge of the user. DO NOT CALL THIS FUNCTION if the `knowledge history` sections appears to be able to answer the user's query.", id: 'knowledge-base-retrieval-tool', @@ -40,7 +43,7 @@ export const KNOWLEDGE_BASE_RETRIEVAL_TOOL: AssistantTool = { return new DynamicStructuredTool({ name: toolDetails.name, - description: toolDetails.description, + description: params.description || toolDetails.description, schema: z.object({ query: z.string().describe(`Summary of items/things to search for in the knowledge base`), }), diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts index 950a22c635036..d3fb2110e7c79 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/knowledge_base/knowledge_base_write_tool.ts @@ -20,6 +20,9 @@ export interface KnowledgeBaseWriteToolParams extends AssistantToolParams { } const toolDetails = { + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: "Call this for writing details to the user's knowledge base. The knowledge base contains useful information the user wants to store between conversation contexts. Input will be the summarized knowledge base entry to store, a short UI friendly name for the entry, and whether or not the entry is required.", id: 'knowledge-base-write-tool', @@ -40,7 +43,7 @@ export const KNOWLEDGE_BASE_WRITE_TOOL: AssistantTool = { return new DynamicStructuredTool({ name: toolDetails.name, - description: toolDetails.description, + description: params.description || toolDetails.description, schema: z.object({ name: z .string() diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts index 5cdc59a5fca94..aeda113abc2f0 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts @@ -18,7 +18,7 @@ import type { ContentReferencesStore, SecurityAlertContentReference, } from '@kbn/elastic-assistant-common'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; const MAX_SIZE = 10000; @@ -44,7 +44,7 @@ describe('OpenAndAcknowledgedAlertsTool', () => { const isEnabledKnowledgeBase = true; const chain = {} as unknown as RetrievalQAChain; const logger = loggerMock.create(); - const contentReferencesStore = contentReferencesStoreFactoryMock(); + const contentReferencesStore = newContentReferencesStoreMock(); const rest = { isEnabledKnowledgeBase, esClient, @@ -274,7 +274,7 @@ describe('OpenAndAcknowledgedAlertsTool', () => { request, size: request.body.size, ...rest, - contentReferencesStore: false, + contentReferencesStore: undefined, }) as DynamicTool; (esClient.search as jest.Mock).mockResolvedValue({ diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts index a8fb0f0064d24..c7ca6972f5476 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts @@ -37,6 +37,9 @@ export const OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION = export const OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL: AssistantTool = { id: 'open-and-acknowledged-alerts-tool', name: 'OpenAndAcknowledgedAlertsTool', + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION, sourceRegister: APP_UI_ID, isSupported: (params: AssistantToolParams): params is OpenAndAcknowledgedAlertsToolParams => { @@ -62,7 +65,7 @@ export const OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL: AssistantTool = { } = params as OpenAndAcknowledgedAlertsToolParams; return new DynamicStructuredTool({ name: 'OpenAndAcknowledgedAlertsTool', - description: OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION, + description: params.description || OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION, schema: z.object({}), func: async () => { const query = getOpenAndAcknowledgedAlertsQuery({ diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.test.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.test.ts index 11a95bd29b594..8d803f3bbaf25 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.test.ts @@ -20,7 +20,7 @@ import type { ContentReferencesStore, ProductDocumentationContentReference, } from '@kbn/elastic-assistant-common'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; describe('ProductDocumentationTool', () => { const chain = {} as RetrievalQAChain; @@ -35,7 +35,7 @@ describe('ProductDocumentationTool', () => { retrieveDocumentationAvailable: jest.fn(), } as LlmTasksPluginStart; const connectorId = 'fake-connector'; - const contentReferencesStore = contentReferencesStoreFactoryMock(); + const contentReferencesStore = newContentReferencesStoreMock(); const defaultArgs = { chain, esClient, @@ -144,7 +144,7 @@ describe('ProductDocumentationTool', () => { it('does not include citations if contentReferencesStore is false', async () => { const tool = PRODUCT_DOCUMENTATION_TOOL.getTool({ ...defaultArgs, - contentReferencesStore: false, + contentReferencesStore: undefined, }) as DynamicStructuredTool; (retrieveDocumentation as jest.Mock).mockResolvedValue({ diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.ts index da3b1ba492693..bcfcadf1ca076 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/product_docs/product_documentation_tool.ts @@ -18,6 +18,9 @@ import type { RetrieveDocumentationResultDoc } from '@kbn/llm-tasks-plugin/serve import { APP_UI_ID } from '../../../../common'; const toolDetails = { + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: 'Use this tool to retrieve documentation about Elastic products. You can retrieve documentation about the Elastic stack, such as Kibana and Elasticsearch, or for Elastic solutions, such as Elastic Security, Elastic Observability or Elastic Enterprise Search.', id: 'product-documentation-tool', @@ -40,7 +43,7 @@ export const PRODUCT_DOCUMENTATION_TOOL: AssistantTool = { return new DynamicStructuredTool({ name: toolDetails.name, - description: toolDetails.description, + description: params.description || toolDetails.description, schema: z.object({ query: z.string().describe( `The query to use to retrieve documentation diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.test.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.test.ts index c8bba6e3a7113..4e7df37d4f904 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.test.ts @@ -11,11 +11,11 @@ import type { ContentReferencesStore, KnowledgeBaseEntryContentReference, } from '@kbn/elastic-assistant-common'; -import { contentReferencesStoreFactoryMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; +import { newContentReferencesStoreMock } from '@kbn/elastic-assistant-common/impl/content_references/content_references_store/__mocks__/content_references_store.mock'; import type { AssistantToolParams } from '@kbn/elastic-assistant-plugin/server'; describe('SecurityLabsTool', () => { - const contentReferencesStore = contentReferencesStoreFactoryMock(); + const contentReferencesStore = newContentReferencesStoreMock(); const getKnowledgeBaseDocumentEntries = jest.fn().mockResolvedValue([]); const kbDataClient = { getKnowledgeBaseDocumentEntries }; const defaultArgs = { @@ -54,7 +54,7 @@ describe('SecurityLabsTool', () => { it('does not include citations when contentReferencesStore is false', async () => { const tool = SECURITY_LABS_KNOWLEDGE_BASE_TOOL.getTool({ ...defaultArgs, - contentReferencesStore: false, + contentReferencesStore: undefined, }) as DynamicStructuredTool; const result = await tool.func({ query: 'What is Kibana Security?', product: 'kibana' }); diff --git a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts index b19fa15747e67..140cc09a670c6 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/assistant/tools/security_labs/security_labs_tool.ts @@ -14,6 +14,9 @@ import { knowledgeBaseReference, contentReferenceString } from '@kbn/elastic-ass import { APP_UI_ID } from '../../../../common'; const toolDetails = { + // note: this description is overwritten when `getTool` is called + // local definitions exist ../elastic_assistant/server/lib/prompt/tool_prompts.ts + // local definitions can be overwritten by security-ai-prompt integration definitions description: 'Call this for knowledge from Elastic Security Labs content, which contains information on malware, attack techniques, and more.', id: 'security-labs-knowledge-base-tool', @@ -34,7 +37,7 @@ export const SECURITY_LABS_KNOWLEDGE_BASE_TOOL: AssistantTool = { return new DynamicStructuredTool({ name: toolDetails.name, - description: toolDetails.description, + description: params.description || toolDetails.description, schema: z.object({ question: z .string() diff --git a/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index 4aa13036c3c88..496056f6878d6 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -69,7 +69,7 @@ import type { } from '@kbn/fleet-plugin/common'; import { createMockPolicyData } from '../endpoint/services/feature_usage/mocks'; import { ALL_ENDPOINT_ARTIFACT_LIST_IDS } from '../../common/endpoint/service/artifacts/constants'; -import { ENDPOINT_EVENT_FILTERS_LIST_ID } from '@kbn/securitysolution-list-constants'; +import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; import * as PolicyConfigHelpers from '../../common/endpoint/models/policy_config_helpers'; import { disableProtections } from '../../common/endpoint/models/policy_config_helpers'; import type { ProductFeaturesService } from '../lib/product_features_service/product_features_service'; @@ -421,12 +421,15 @@ describe('Fleet integrations', () => { ); expect(exceptionListClient.createExceptionList).toHaveBeenCalledWith( - expect.objectContaining({ listId: ENDPOINT_EVENT_FILTERS_LIST_ID }) + expect.objectContaining({ + listId: ENDPOINT_ARTIFACT_LISTS.eventFilters.id, + meta: undefined, + }) ); expect(exceptionListClient.createExceptionListItem).toHaveBeenCalledWith( expect.objectContaining({ - listId: ENDPOINT_EVENT_FILTERS_LIST_ID, + listId: ENDPOINT_ARTIFACT_LISTS.eventFilters.id, tags: [`policy:${postCreatedPolicyConfig.id}`], osTypes: ['linux'], entries: [ @@ -439,6 +442,7 @@ describe('Fleet integrations', () => { ], itemId: 'NEW_UUID', namespaceType: 'agnostic', + meta: undefined, }) ); }); diff --git a/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts b/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts index 5e5cefd5d2a90..bd9aa21045d17 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/fleet_integration/handlers/create_event_filters.ts @@ -6,11 +6,7 @@ */ import { v4 as uuidv4 } from 'uuid'; import { i18n } from '@kbn/i18n'; -import { - ENDPOINT_EVENT_FILTERS_LIST_ID, - ENDPOINT_EVENT_FILTERS_LIST_NAME, - ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION, -} from '@kbn/securitysolution-list-constants'; +import { ENDPOINT_ARTIFACT_LISTS } from '@kbn/securitysolution-list-constants'; import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { Logger } from '@kbn/core/server'; @@ -37,10 +33,10 @@ export const createEventFilters = async ( // Attempt to Create the Event Filter List. It won't create the list if it already exists. // So we can skip the validation and ignore the conflict error await exceptionsClient.createExceptionList({ - name: ENDPOINT_EVENT_FILTERS_LIST_NAME, + name: ENDPOINT_ARTIFACT_LISTS.eventFilters.name, namespaceType: 'agnostic', - description: ENDPOINT_EVENT_FILTERS_LIST_DESCRIPTION, - listId: ENDPOINT_EVENT_FILTERS_LIST_ID, + description: ENDPOINT_ARTIFACT_LISTS.eventFilters.description, + listId: ENDPOINT_ARTIFACT_LISTS.eventFilters.id, type: ExceptionListTypeEnum.ENDPOINT_EVENTS, immutable: false, meta: undefined, @@ -61,14 +57,14 @@ export const createEventFilters = async ( /** * Create an Event Filter for non-interactive sessions and attach it to the policy */ -export const createNonInteractiveSessionEventFilter = async ( +const createNonInteractiveSessionEventFilter = async ( logger: Logger, exceptionsClient: ExceptionListClient, packagePolicy: PackagePolicy ): Promise => { try { await exceptionsClient.createExceptionListItem({ - listId: ENDPOINT_EVENT_FILTERS_LIST_ID, + listId: ENDPOINT_ARTIFACT_LISTS.eventFilters.id, description: i18n.translate( 'xpack.securitySolution.fleetIntegration.elasticDefend.eventFilter.nonInteractiveSessions.description', { @@ -95,7 +91,7 @@ export const createNonInteractiveSessionEventFilter = async ( }, ], itemId: uuidv4(), - meta: [], + meta: undefined, comments: [], expireTime: undefined, }); diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_sender.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_sender.ts index 66161274fe45c..ad142b9032903 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_sender.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_sender.ts @@ -167,11 +167,14 @@ export class PreviewTelemetryEventsSender implements ITelemetryEventsSender { } public sendAsync(channel: TelemetryChannel, events: unknown[]): void { - this.composite.sendAsync(channel, events); + const result = this.composite.simulateSendAsync(channel, events); + this.sentMessages = [...this.sentMessages, ...result]; } public simulateSendAsync(channel: TelemetryChannel, events: unknown[]): string[] { - return this.composite.simulateSendAsync(channel, events); + const result = this.composite.simulateSendAsync(channel, events); + this.sentMessages = [...this.sentMessages, ...result]; + return result; } public updateQueueConfig(channel: TelemetryChannel, config: QueueConfig): void { diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_task_metrics.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_task_metrics.ts index 96b23e1ebe087..ded852929ff80 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_task_metrics.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/preview_task_metrics.ts @@ -34,19 +34,18 @@ export class PreviewTaskMetricsService implements ITaskMetricsService { } public start(name: string): Trace { - this.logger.error('Simulating TaskMetricsService.start'); + this.logger.info('Simulating TaskMetricsService.start'); return this.composite.start(name); } public createTaskMetric(trace: Trace, error?: Error): TaskMetric { - this.logger.error('Simulating TaskMetricsService.createTaskMetric'); + this.logger.info('Simulating TaskMetricsService.createTaskMetric'); return this.composite.createTaskMetric(trace, error); } public async end(trace: Trace, error?: Error): Promise { - this.logger.error('Simulating TaskMetricsService.end'); + this.logger.info('Simulating TaskMetricsService.end'); const metric = this.composite.createTaskMetric(trace, error); - const result = this.sender.simulateSendAsync(TelemetryChannel.TASK_METRICS, [metric]); - this.sentMessages = [...this.sentMessages, ...result]; + this.sender.simulateSendAsync(TelemetryChannel.TASK_METRICS, [metric]); } } diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts index aceb3ebf98926..7b830ade4a02c 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/tasks/endpoint.ts @@ -312,6 +312,7 @@ class EndpointMetadataProcessor { system_impact: systemImpact, threads, event_filter: eventFilter, + top_process_trees: topProcessTrees, } = endpointMetric.Endpoint.metrics; const endpointPolicyDetail = extractEndpointPolicyConfig(policyConfig); if (endpointPolicyDetail) { @@ -336,6 +337,7 @@ class EndpointMetadataProcessor { systemImpact, threads, eventFilter, + topProcessTrees, }, endpoint_meta: { os: endpointMetric.host.os, diff --git a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/types.ts b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/types.ts index 7741db5721c78..44078855d85ee 100644 --- a/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/types.ts +++ b/x-pack/solutions/security/plugins/security_solution/server/lib/telemetry/types.ts @@ -281,6 +281,22 @@ export interface EndpointMetrics { active_global_count: number; active_user_count: number; }; + top_process_trees: { + values: Event[]; + }; +} + +interface Event { + event_count: number; + last_seen: string; + sample: Sample; +} + +interface Sample { + command_line: string; + entity_id: string; + executable: string; + parent_command_line: string; } interface EndpointMetricOS { diff --git a/x-pack/solutions/security/plugins/timelines/common/constants.ts b/x-pack/solutions/security/plugins/timelines/common/constants.ts index 779e32584a64e..c076f8b0b6942 100644 --- a/x-pack/solutions/security/plugins/timelines/common/constants.ts +++ b/x-pack/solutions/security/plugins/timelines/common/constants.ts @@ -9,6 +9,3 @@ export const DEFAULT_MAX_TABLE_QUERY_SIZE = 10000; export const DELETED_SECURITY_SOLUTION_DATA_VIEW = 'DELETED_SECURITY_SOLUTION_DATA_VIEW'; export const ENRICHMENT_DESTINATION_PATH = 'threat.enrichments'; - -/** The default minimum width of a column (when a width for the column type is not specified) */ -export const DEFAULT_COLUMN_MIN_WIDTH = 180; // px diff --git a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx b/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx deleted file mode 100644 index 728d144d91e3c..0000000000000 --- a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/column_toggle.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useEffect, useMemo } from 'react'; -import { EuiContextMenuItem, EuiButtonEmpty, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../../common/constants'; -import { ColumnHeaderOptions, defaultColumnHeaderType } from '../../../../common/types'; -import { stopPropagationAndPreventDefault } from '../../../../common/utils/accessibility'; -import { TooltipWithKeyboardShortcut } from '../../tooltip_with_keyboard_shortcut'; -import { getAdditionalScreenReaderOnlyContext } from '../utils'; -import { HoverActionComponentProps } from './types'; - -export const COLUMN_TOGGLE = (field: string) => - i18n.translate('xpack.timelines.hoverActions.columnToggleLabel', { - values: { field }, - defaultMessage: 'Toggle {field} column in table', - }); - -export const NESTED_COLUMN = (field: string) => - i18n.translate('xpack.timelines.hoverActions.nestedColumnToggleLabel', { - values: { field }, - defaultMessage: - 'The {field} field is an object, and is broken down into nested fields which can be added as columns', - }); - -export const COLUMN_TOGGLE_KEYBOARD_SHORTCUT = 'i'; - -export interface ColumnToggleProps extends HoverActionComponentProps { - Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon | typeof EuiContextMenuItem; - isDisabled: boolean; - isObjectArray: boolean; - toggleColumn: (column: ColumnHeaderOptions) => void; -} - -const ColumnToggleButton: React.FC = React.memo( - ({ - Component, - defaultFocusedButtonRef, - field, - isDisabled, - isObjectArray, - keyboardEvent, - ownFocus, - onClick, - showTooltip = false, - toggleColumn, - value, - }) => { - const label = isObjectArray ? NESTED_COLUMN(field) : COLUMN_TOGGLE(field); - - const handleToggleColumn = useCallback(() => { - toggleColumn({ - columnHeaderType: defaultColumnHeaderType, - id: field, - initialWidth: DEFAULT_COLUMN_MIN_WIDTH, - }); - if (onClick != null) { - onClick(); - } - }, [onClick, field, toggleColumn]); - - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === COLUMN_TOGGLE_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - handleToggleColumn(); - } - }, [handleToggleColumn, keyboardEvent, ownFocus]); - - const button = useMemo( - () => - Component ? ( - - {label} - - ) : ( - - ), - [Component, defaultFocusedButtonRef, field, handleToggleColumn, isDisabled, label] - ); - - return showTooltip ? ( - - } - > - {button} - - ) : ( - button - ); - } -); - -ColumnToggleButton.displayName = 'ColumnToggleButton'; - -// eslint-disable-next-line import/no-default-export -export { ColumnToggleButton as default }; diff --git a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/overflow.test.tsx b/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/overflow.test.tsx deleted file mode 100644 index e5d53df638829..0000000000000 --- a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/overflow.test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; -import OverflowButton from './overflow'; - -describe('OverflowButton', () => { - const props = { - field: 'host.name', - ownFocus: false, - showTooltip: true, - value: 'mac', - closePopOver: jest.fn(), - items: [
], - isOverflowPopoverOpen: false, - }; - test('should render a popover', () => { - const wrapper = shallow(); - expect(wrapper.find('EuiPopover').exists()).toBeTruthy(); - }); - - test('the popover always contains a class that hides it when an overlay (e.g. the inspect modal) is displayed', () => { - const wrapper = shallow(); - expect(wrapper.find('EuiPopover').prop('panelClassName')).toEqual('withHoverActions__popover'); - }); - - test('should enable repositionOnScroll', () => { - const wrapper = shallow(); - expect(wrapper.find('EuiPopover').prop('repositionOnScroll')).toEqual(true); - }); - - test('should render a tooltip if showTooltip is true', () => { - const testProps = { - ...props, - showTooltip: true, - }; - const wrapper = shallow(); - expect(wrapper.find('EuiToolTip').exists()).toBeTruthy(); - }); -}); diff --git a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/overflow.tsx b/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/overflow.tsx deleted file mode 100644 index ffd68a8c5efbf..0000000000000 --- a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/actions/overflow.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useEffect, useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { - EuiButtonIcon, - EuiButtonEmpty, - EuiContextMenuPanel, - EuiContextMenuItem, - EuiPopover, - EuiToolTip, -} from '@elastic/eui'; - -import styled from 'styled-components'; -import { stopPropagationAndPreventDefault } from '../../../../common/utils/accessibility'; -import { TooltipWithKeyboardShortcut } from '../../tooltip_with_keyboard_shortcut'; -import { getAdditionalScreenReaderOnlyContext } from '../utils'; -import { HoverActionComponentProps } from './types'; - -export const MORE_ACTIONS = i18n.translate('xpack.timelines.hoverActions.moreActions', { - defaultMessage: 'More actions', -}); - -export const FILTER_OUT_VALUE_KEYBOARD_SHORTCUT = 'm'; - -export interface OverflowButtonProps extends HoverActionComponentProps { - closePopOver: () => void; - Component?: typeof EuiButtonEmpty | typeof EuiButtonIcon | typeof EuiContextMenuItem; - items: JSX.Element[]; - isOverflowPopoverOpen: boolean; -} - -const StyledEuiContextMenuPanel = styled(EuiContextMenuPanel)` - visibility: inherit; -`; - -const OverflowButton: React.FC = React.memo( - ({ - closePopOver, - Component, - defaultFocusedButtonRef, - field, - items, - isOverflowPopoverOpen, - keyboardEvent, - ownFocus, - onClick, - showTooltip = false, - value, - }) => { - useEffect(() => { - if (!ownFocus) { - return; - } - if (keyboardEvent?.key === FILTER_OUT_VALUE_KEYBOARD_SHORTCUT) { - stopPropagationAndPreventDefault(keyboardEvent); - if (onClick != null) { - onClick(); - } - } - }, [keyboardEvent, onClick, ownFocus]); - - const popover = useMemo( - () => ( - - {MORE_ACTIONS} - - ) : ( - - ) - } - isOpen={isOverflowPopoverOpen} - closePopover={closePopOver} - panelPaddingSize="none" - panelClassName="withHoverActions__popover" - repositionOnScroll={true} - anchorPosition="downLeft" - > - - - ), - [ - Component, - defaultFocusedButtonRef, - field, - onClick, - isOverflowPopoverOpen, - closePopOver, - items, - ] - ); - - return showTooltip ? ( - - } - > - {popover} - - ) : ( - popover - ); - } -); - -OverflowButton.displayName = 'OverflowButton'; - -// eslint-disable-next-line import/no-default-export -export { OverflowButton as default }; diff --git a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/index.tsx b/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/index.tsx index f2089deed7b4b..7c61ef94bf91d 100644 --- a/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/index.tsx +++ b/x-pack/solutions/security/plugins/timelines/public/components/hover_actions/index.tsx @@ -10,16 +10,13 @@ import React, { ReactElement } from 'react'; import { Provider } from 'react-redux'; import { Store } from 'redux'; import type { AddToTimelineButtonProps } from './actions/add_to_timeline'; -import type { ColumnToggleProps } from './actions/column_toggle'; import type { CopyProps } from './actions/copy'; -import type { HoverActionComponentProps, FilterValueFnArgs } from './actions/types'; -import type { OverflowButtonProps } from './actions/overflow'; +import type { FilterValueFnArgs, HoverActionComponentProps } from './actions/types'; export interface HoverActionsConfig { getAddToTimelineButton: ( props: AddToTimelineButtonProps ) => ReactElement; - getColumnToggleButton: (props: ColumnToggleProps) => ReactElement; getCopyButton: (props: CopyProps) => ReactElement; getFilterForValueButton: ( props: HoverActionComponentProps & FilterValueFnArgs @@ -27,7 +24,6 @@ export interface HoverActionsConfig { getFilterOutValueButton: ( props: HoverActionComponentProps & FilterValueFnArgs ) => ReactElement; - getOverflowButton: (props: OverflowButtonProps) => ReactElement; } const AddToTimelineButtonLazy = React.lazy(() => import('./actions/add_to_timeline')); @@ -43,15 +39,6 @@ const getAddToTimelineButtonLazy = (store: Store, props: AddToTimelineButtonProp ); }; -const ColumnToggleButtonLazy = React.lazy(() => import('./actions/column_toggle')); -const getColumnToggleButtonLazy = (props: ColumnToggleProps) => { - return ( - }> - - - ); -}; - const CopyButtonLazy = React.lazy(() => import('./actions/copy')); const getCopyButtonLazy = (props: CopyProps) => { return ( @@ -79,21 +66,10 @@ const getFilterOutValueButtonLazy = (props: HoverActionComponentProps & FilterVa ); }; -const OverflowButtonLazy = React.lazy(() => import('./actions/overflow')); -const getOverflowButtonLazy = (props: OverflowButtonProps) => { - return ( - }> - - - ); -}; - export const getHoverActions = (store?: Store): HoverActionsConfig => ({ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion getAddToTimelineButton: getAddToTimelineButtonLazy.bind(null, store!), - getColumnToggleButton: getColumnToggleButtonLazy, getCopyButton: getCopyButtonLazy, getFilterForValueButton: getFilterForValueButtonLazy, getFilterOutValueButton: getFilterOutValueButtonLazy, - getOverflowButton: getOverflowButtonLazy, }); diff --git a/x-pack/solutions/security/plugins/timelines/public/components/index.tsx b/x-pack/solutions/security/plugins/timelines/public/components/index.tsx index 323afe334093f..0964b94434605 100644 --- a/x-pack/solutions/security/plugins/timelines/public/components/index.tsx +++ b/x-pack/solutions/security/plugins/timelines/public/components/index.tsx @@ -6,4 +6,3 @@ */ export * from './last_updated'; -export * from './loading'; diff --git a/x-pack/solutions/security/plugins/timelines/public/components/loading/index.tsx b/x-pack/solutions/security/plugins/timelines/public/components/loading/index.tsx deleted file mode 100644 index 652cb6a5dae33..0000000000000 --- a/x-pack/solutions/security/plugins/timelines/public/components/loading/index.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiPanel, EuiText } from '@elastic/eui'; -import React from 'react'; -import styled from 'styled-components'; - -const SpinnerFlexItem = styled(EuiFlexItem)` - margin-right: 5px; -`; - -SpinnerFlexItem.displayName = 'SpinnerFlexItem'; - -export interface LoadingPanelProps { - dataTestSubj?: string; - text: string | React.ReactNode; - height: number | string; - showBorder?: boolean; - width: number | string; - zIndex?: number | string; - position?: string; -} - -export const LoadingPanel = React.memo( - ({ - dataTestSubj = '', - height = 'auto', - showBorder = true, - text, - width, - position = 'relative', - zIndex = 'inherit', - }) => ( - - - - - - - - - - {text} - - - - - - ) -); - -LoadingPanel.displayName = 'LoadingPanel'; - -export const LoadingStaticPanel = styled.div<{ - height: number | string; - position: string; - width: number | string; - zIndex: number | string; -}>` - height: ${({ height }) => height}; - position: ${({ position }) => position}; - width: ${({ width }) => width}; - overflow: hidden; - display: flex; - flex-direction: column; - justify-content: center; - z-index: ${({ zIndex }) => zIndex}; -`; - -LoadingStaticPanel.displayName = 'LoadingStaticPanel'; - -export const LoadingStaticContentPanel = styled.div` - flex: 0 0 auto; - align-self: center; - text-align: center; - height: fit-content; - .euiPanel.euiPanel--paddingMedium { - padding: 10px; - } -`; - -LoadingStaticContentPanel.displayName = 'LoadingStaticContentPanel'; - -// eslint-disable-next-line import/no-default-export -export { LoadingPanel as default }; diff --git a/x-pack/solutions/security/plugins/timelines/public/methods/index.tsx b/x-pack/solutions/security/plugins/timelines/public/methods/index.tsx index dfd68e0e2361a..8f1fd35a56ad3 100644 --- a/x-pack/solutions/security/plugins/timelines/public/methods/index.tsx +++ b/x-pack/solutions/security/plugins/timelines/public/methods/index.tsx @@ -7,7 +7,7 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import { LastUpdatedAtProps, LoadingPanelProps } from '../components'; +import { LastUpdatedAtProps } from '../components'; const LastUpdatedLazy = lazy(() => import('../components/last_updated')); export const getLastUpdatedLazy = (props: LastUpdatedAtProps) => { @@ -17,12 +17,3 @@ export const getLastUpdatedLazy = (props: LastUpdatedAtProps) => { ); }; - -const LoadingPanelLazy = lazy(() => import('../components/loading')); -export const getLoadingPanelLazy = (props: LoadingPanelProps) => { - return ( - }> - - - ); -}; diff --git a/x-pack/solutions/security/plugins/timelines/public/mock/mock_hover_actions.tsx b/x-pack/solutions/security/plugins/timelines/public/mock/mock_hover_actions.tsx index 22ab99c35cf92..52c4113ceb1ef 100644 --- a/x-pack/solutions/security/plugins/timelines/public/mock/mock_hover_actions.tsx +++ b/x-pack/solutions/security/plugins/timelines/public/mock/mock_hover_actions.tsx @@ -10,15 +10,9 @@ export const mockHoverActions = { getAddToTimelineButton: () => ( {'Add To Timeline'} ), - getColumnToggleButton: () => {'Column Toggle'}, getCopyButton: () => {'Copy button'}, getFilterForValueButton: () => {'Filter button'}, getFilterOutValueButton: () => ( {'Filter out button'} ), - getOverflowButton: (props: { field: string }) => ( -
- {'Overflow button'} -
- ), }; diff --git a/x-pack/solutions/security/plugins/timelines/public/mock/plugin_mock.tsx b/x-pack/solutions/security/plugins/timelines/public/mock/plugin_mock.tsx index 2813acb840966..2113369a76e16 100644 --- a/x-pack/solutions/security/plugins/timelines/public/mock/plugin_mock.tsx +++ b/x-pack/solutions/security/plugins/timelines/public/mock/plugin_mock.tsx @@ -5,14 +5,13 @@ * 2.0. */ import React from 'react'; -import { LastUpdatedAt, LastUpdatedAtProps, LoadingPanel, LoadingPanelProps } from '../components'; +import { LastUpdatedAt, LastUpdatedAtProps } from '../components'; import { useAddToTimeline, useAddToTimelineSensor } from '../hooks/use_add_to_timeline'; import { mockHoverActions } from './mock_hover_actions'; export const createTGridMocks = () => ({ getHoverActions: () => mockHoverActions, getLastUpdated: (props: LastUpdatedAtProps) => , - getLoadingPanel: (props: LoadingPanelProps) => , getUseAddToTimeline: () => useAddToTimeline, getUseAddToTimelineSensor: () => useAddToTimelineSensor, }); diff --git a/x-pack/solutions/security/plugins/timelines/public/plugin.ts b/x-pack/solutions/security/plugins/timelines/public/plugin.ts index 8de44309769da..eb34612c40bd8 100644 --- a/x-pack/solutions/security/plugins/timelines/public/plugin.ts +++ b/x-pack/solutions/security/plugins/timelines/public/plugin.ts @@ -6,13 +6,13 @@ */ import { Store, Unsubscribe } from 'redux'; -import type { CoreSetup, Plugin, CoreStart } from '@kbn/core/public'; -import { getLastUpdatedLazy, getLoadingPanelLazy } from './methods'; -import type { TimelinesUIStart, TimelinesStartPlugins } from './types'; +import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { getLastUpdatedLazy } from './methods'; +import type { TimelinesStartPlugins, TimelinesUIStart } from './types'; import { useAddToTimeline, useAddToTimelineSensor } from './hooks/use_add_to_timeline'; import { getHoverActions, HoverActionsConfig } from './components/hover_actions'; import { timelineReducer } from './store/timeline/reducer'; -import { LastUpdatedAtProps, LoadingPanelProps } from './components'; +import { LastUpdatedAtProps } from './components'; export class TimelinesPlugin implements Plugin { private _store: Store | undefined; @@ -36,9 +36,6 @@ export class TimelinesPlugin implements Plugin { getTimelineReducer: () => { return timelineReducer; }, - getLoadingPanel: (props: LoadingPanelProps) => { - return getLoadingPanelLazy(props); - }, getLastUpdated: (props: LastUpdatedAtProps) => { return getLastUpdatedLazy(props); }, diff --git a/x-pack/solutions/security/plugins/timelines/public/types.ts b/x-pack/solutions/security/plugins/timelines/public/types.ts index 4dabbe9ceb512..233f230be06c3 100644 --- a/x-pack/solutions/security/plugins/timelines/public/types.ts +++ b/x-pack/solutions/security/plugins/timelines/public/types.ts @@ -12,10 +12,10 @@ import { CoreStart } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { CasesPublicStart } from '@kbn/cases-plugin/public'; import { ApmBase } from '@elastic/apm-rum'; -import type { UseAddToTimelineProps, UseAddToTimeline } from './hooks/use_add_to_timeline'; +import type { UseAddToTimeline, UseAddToTimelineProps } from './hooks/use_add_to_timeline'; import { HoverActionsConfig } from './components/hover_actions'; import { LastUpdatedAtProps } from './components/last_updated'; -import { LoadingPanelProps } from './components/loading'; + export interface TimelinesUIStart { /** * @deprecated Use cell-actions package instead @@ -23,7 +23,6 @@ export interface TimelinesUIStart { getHoverActions: () => HoverActionsConfig; // eslint-disable-next-line @typescript-eslint/no-explicit-any getTimelineReducer: () => any; - getLoadingPanel: (props: LoadingPanelProps) => ReactElement; getLastUpdated: (props: LastUpdatedAtProps) => ReactElement; getUseAddToTimeline: () => (props: UseAddToTimelineProps) => UseAddToTimeline; getUseAddToTimelineSensor: () => (api: SensorAPI) => void; diff --git a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts index d7b295e7fda54..25797d52e03cc 100644 --- a/x-pack/test/api_integration/apis/management/index_management/data_streams.ts +++ b/x-pack/test/api_integration/apis/management/index_management/data_streams.ts @@ -26,8 +26,7 @@ export default function ({ getService }: FtrProviderContext) { getDatastream, } = datastreamsHelpers(getService); - // Failing: See https://github.com/elastic/kibana/issues/209014 - describe.skip('Data streams', function () { + describe('Data streams', function () { describe('Get', () => { const testDataStreamName = 'test-data-stream'; @@ -194,14 +193,22 @@ export default function ({ getService }: FtrProviderContext) { await deleteDataStream(logsdbDataStreamName); }); - const logsdbSettings: Array<{ enabled: boolean | null; indexMode: string }> = [ - { enabled: true, indexMode: 'logsdb' }, - { enabled: false, indexMode: 'standard' }, - { enabled: null, indexMode: 'standard' }, // In stateful Kibana, the cluster.logsdb.enabled setting is false by default, so standard index mode + const logsdbSettings: Array<{ + enabled: boolean | null; + prior_logs_usage: boolean; + indexMode: string; + }> = [ + { enabled: true, prior_logs_usage: true, indexMode: 'logsdb' }, + { enabled: false, prior_logs_usage: true, indexMode: 'standard' }, + // In stateful Kibana, if prior_logs_usage is set to true, the cluster.logsdb.enabled setting is false by default, so standard index mode + { enabled: null, prior_logs_usage: true, indexMode: 'standard' }, + // In stateful Kibana, if prior_logs_usage is set to false, the cluster.logsdb.enabled setting is true by default, so logsdb index mode + { enabled: null, prior_logs_usage: false, indexMode: 'logsdb' }, ]; - logsdbSettings.forEach(({ enabled, indexMode }) => { - it(`returns ${indexMode} index mode if logsdb.enabled setting is ${enabled}`, async () => { + // eslint-disable-next-line @typescript-eslint/naming-convention + logsdbSettings.forEach(({ enabled, prior_logs_usage, indexMode }) => { + it(`returns ${indexMode} index mode if logsdb.enabled setting is ${enabled} and logs.prior_logs_usage is ${prior_logs_usage}`, async () => { await es.cluster.putSettings({ body: { persistent: { @@ -210,6 +217,9 @@ export default function ({ getService }: FtrProviderContext) { enabled, }, }, + logsdb: { + prior_logs_usage, + }, }, }, }); diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.ts b/x-pack/test/api_integration/apis/management/index_management/templates.ts index da113cb55ca66..49ed3dfb7158d 100644 --- a/x-pack/test/api_integration/apis/management/index_management/templates.ts +++ b/x-pack/test/api_integration/apis/management/index_management/templates.ts @@ -28,8 +28,7 @@ export default function ({ getService }: FtrProviderContext) { simulateTemplateByName, } = templatesApi(getService); - // Failing: See https://github.com/elastic/kibana/issues/209027 - describe.skip('index templates', () => { + describe('index templates', () => { after(async () => await cleanUpTemplates()); describe('get all', () => { @@ -247,13 +246,21 @@ export default function ({ getService }: FtrProviderContext) { await deleteTemplates([{ name: logsdbTemplateName }]); }); - const logsdbSettings: Array<{ enabled: boolean | null; indexMode: string }> = [ - { enabled: true, indexMode: 'logsdb' }, - { enabled: false, indexMode: 'standard' }, - { enabled: null, indexMode: 'standard' }, // In stateful Kibana, the cluster.logsdb.enabled setting is false by default, so standard index mode + const logsdbSettings: Array<{ + enabled: boolean | null; + prior_logs_usage: boolean; + indexMode: string; + }> = [ + { enabled: true, prior_logs_usage: true, indexMode: 'logsdb' }, + { enabled: false, prior_logs_usage: true, indexMode: 'standard' }, + // In stateful Kibana, if prior_logs_usage is set to true, the cluster.logsdb.enabled setting is false by default, so standard index mode + { enabled: null, prior_logs_usage: true, indexMode: 'standard' }, + // In stateful Kibana, if prior_logs_usage is set to false, the cluster.logsdb.enabled setting is true by default, so logsdb index mode + { enabled: null, prior_logs_usage: false, indexMode: 'logsdb' }, ]; - logsdbSettings.forEach(({ enabled, indexMode }) => { + // eslint-disable-next-line @typescript-eslint/naming-convention + logsdbSettings.forEach(({ enabled, prior_logs_usage, indexMode }) => { it(`returns ${indexMode} index mode if logsdb.enabled setting is ${enabled}`, async () => { await es.cluster.putSettings({ body: { @@ -263,6 +270,9 @@ export default function ({ getService }: FtrProviderContext) { enabled, }, }, + logsdb: { + prior_logs_usage, + }, }, }, }); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/cardinality_runtime_field_fired.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/cardinality_runtime_field_fired.ts new file mode 100644 index 0000000000000..b40cf2e8a1e14 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/cardinality_runtime_field_fired.ts @@ -0,0 +1,292 @@ +/* + * Copyright 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 { omit } from 'lodash'; +import expect from '@kbn/expect'; +import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; +import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { kbnTestConfig } from '@kbn/test'; +import type { InternalRequestHeader, RoleCredentials } from '@kbn/ftr-common-functional-services'; +import { DeploymentAgnosticFtrProviderContext } from '../../../../ftr_provider_context'; +import { ISO_DATE_REGEX } from './constants'; +import { ActionDocument, LogsExplorerLocatorParsedParams } from './types'; + +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const esClient = getService('es'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const esDeleteAllIndices = getService('esDeleteAllIndices'); + const logger = getService('log'); + const alertingApi = getService('alertingApi'); + const dataViewApi = getService('dataViewApi'); + const samlAuth = getService('samlAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; + const config = getService('config'); + const isServerless = config.get('serverless'); + const expectedConsumer = isServerless ? 'observability' : 'logs'; + + describe('CARDINALITY - RUNTIME FIELD - FIRED', () => { + const CUSTOM_THRESHOLD_RULE_ALERT_INDEX = '.alerts-observability.threshold.alerts-default'; + const ALERT_ACTION_INDEX = 'alert-action-threshold'; + const DATA_VIEW = 'kbn-data-forge-fake_hosts.fake_hosts-*'; + const DATA_VIEW_ID = 'data-view-id'; + const DATA_VIEW_NAME = 'data-view-name'; + const runtimeMappings = { + runtimeHostName: { + type: 'keyword', + script: { + source: + "String runtimeHostName = doc['host.name'].value;\n" + '\n' + 'emit(runtimeHostName);', + }, + }, + }; + let dataForgeConfig: PartialConfig; + let dataForgeIndices: string[]; + let actionId: string; + let ruleId: string; + let alertId: string; + + before(async () => { + roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); + internalReqHeader = samlAuth.getInternalRequestHeader(); + dataForgeConfig = { + schedule: [ + { + template: 'good', + start: 'now-10m', + end: 'now+5m', + metrics: [ + { name: 'system.cpu.user.pct', method: 'linear', start: 2.5, end: 2.5 }, + { name: 'system.cpu.total.pct', method: 'linear', start: 0.5, end: 0.5 }, + { name: 'system.cpu.total.norm.pct', method: 'linear', start: 0.8, end: 0.8 }, + ], + }, + ], + indexing: { + dataset: 'fake_hosts' as Dataset, + eventsPerCycle: 1, + interval: 60000, + alignEventsToInterval: true, + }, + }; + dataForgeIndices = await generate({ client: esClient, config: dataForgeConfig, logger }); + await alertingApi.waitForDocumentInIndex({ + indexName: dataForgeIndices.join(','), + docCountTarget: 45, + }); + await dataViewApi.create({ + name: DATA_VIEW_NAME, + id: DATA_VIEW_ID, + title: DATA_VIEW, + roleAuthc, + data: { + runtimeFieldMap: JSON.stringify(runtimeMappings), + }, + }); + }); + + after(async () => { + await supertestWithoutAuth + .delete(`/api/alerting/rule/${ruleId}`) + .set(roleAuthc.apiKeyHeader) + .set(internalReqHeader); + await supertestWithoutAuth + .delete(`/api/actions/connector/${actionId}`) + .set(roleAuthc.apiKeyHeader) + .set(internalReqHeader); + await esClient.deleteByQuery({ + index: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + query: { term: { 'kibana.alert.rule.uuid': ruleId } }, + conflicts: 'proceed', + }); + await esClient.deleteByQuery({ + index: '.kibana-event-log-*', + query: { term: { 'rule.id': ruleId } }, + conflicts: 'proceed', + }); + await dataViewApi.delete({ + id: DATA_VIEW_ID, + roleAuthc, + }); + await esDeleteAllIndices([ALERT_ACTION_INDEX, ...dataForgeIndices]); + await cleanup({ client: esClient, config: dataForgeConfig, logger }); + await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc); + }); + + describe('Rule creation', () => { + it('creates rule successfully', async () => { + actionId = await alertingApi.createIndexConnector({ + roleAuthc, + name: 'Index Connector: Threshold API test', + indexName: ALERT_ACTION_INDEX, + }); + + const createdRule = await alertingApi.createRule({ + roleAuthc, + tags: ['observability'], + consumer: expectedConsumer, + name: 'Threshold rule', + ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + params: { + criteria: [ + { + comparator: COMPARATORS.GREATER_THAN, + threshold: [0], + timeSize: 1, + timeUnit: 'm', + metrics: [ + { name: 'A', field: 'runtimeHostName', aggType: Aggregators.CARDINALITY }, + ], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: DATA_VIEW_ID, + }, + }, + actions: [ + { + group: FIRED_ACTIONS_ID, + id: actionId, + params: { + documents: [ + { + ruleType: '{{rule.type}}', + alertDetailsUrl: '{{context.alertDetailsUrl}}', + reason: '{{context.reason}}', + value: '{{context.value}}', + viewInAppUrl: '{{context.viewInAppUrl}}', + }, + ], + }, + frequency: { + notify_when: 'onActionGroupChange', + throttle: null, + summary: false, + }, + }, + ], + }); + ruleId = createdRule.id; + expect(ruleId).not.to.be(undefined); + }); + + it('should be active', async () => { + const executionStatus = await alertingApi.waitForRuleStatus({ + roleAuthc, + ruleId, + expectedStatus: 'active', + }); + expect(executionStatus).to.be('active'); + }); + + it('should find the created rule with correct information about the consumer', async () => { + const match = await alertingApi.findInRules(roleAuthc, ruleId); + expect(match).not.to.be(undefined); + expect(match.consumer).to.be(expectedConsumer); + }); + + it('should set correct information in the alert document', async () => { + const resp = await alertingApi.waitForAlertInIndex({ + indexName: CUSTOM_THRESHOLD_RULE_ALERT_INDEX, + ruleId, + }); + alertId = (resp.hits.hits[0]._source as any)['kibana.alert.uuid']; + + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.category', + 'Custom threshold' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.consumer', expectedConsumer); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.name', 'Threshold rule'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.producer', 'observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.revision', 0); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.rule.rule_type_id', + 'observability.rules.custom_threshold' + ); + expect(resp.hits.hits[0]._source).property('kibana.alert.rule.uuid', ruleId); + expect(resp.hits.hits[0]._source).property('kibana.space_ids').contain('default'); + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.tags') + .contain('observability'); + expect(resp.hits.hits[0]._source).property( + 'kibana.alert.action_group', + 'custom_threshold.fired' + ); + expect(resp.hits.hits[0]._source).property('tags').contain('observability'); + expect(resp.hits.hits[0]._source).property('kibana.alert.instance.id', '*'); + expect(resp.hits.hits[0]._source).property('kibana.alert.workflow_status', 'open'); + expect(resp.hits.hits[0]._source).property('event.kind', 'signal'); + expect(resp.hits.hits[0]._source).property('event.action', 'open'); + expect(resp.hits.hits[0]._source).not.have.property('kibana.alert.group'); + expect(resp.hits.hits[0]._source).property('kibana.alert.evaluation.threshold').eql([0]); + expect(resp.hits.hits[0]._source) + .property('kibana.alert.rule.parameters') + .eql({ + criteria: [ + { + comparator: COMPARATORS.GREATER_THAN, + threshold: [0], + timeSize: 1, + timeUnit: 'm', + metrics: [{ name: 'A', field: 'runtimeHostName', aggType: 'cardinality' }], + }, + ], + alertOnNoData: true, + alertOnGroupDisappear: true, + searchConfiguration: { + index: 'data-view-id', + query: { query: '', language: 'kuery' }, + }, + }); + }); + + it('should set correct action variables', async () => { + const resp = await alertingApi.waitForDocumentInIndex({ + indexName: ALERT_ACTION_INDEX, + docCountTarget: 1, + }); + + const { protocol, hostname, port } = kbnTestConfig.getUrlPartsWithStrippedDefaultPort(); + + expect(resp.hits.hits[0]._source?.ruleType).eql('observability.rules.custom_threshold'); + expect(resp.hits.hits[0]._source?.alertDetailsUrl).eql( + `${protocol}://${hostname}${port ? `:${port}` : ''}/app/observability/alerts/${alertId}` + ); + + expect(resp.hits.hits[0]._source?.reason).eql( + `Cardinality of the runtimeHostName is 1, above the threshold of 0. (duration: 1 min, data view: ${DATA_VIEW_NAME})` + ); + expect(resp.hits.hits[0]._source?.value).eql('1'); + + const parsedViewInAppUrl = parseSearchParams( + new URL(resp.hits.hits[0]._source?.viewInAppUrl || '').search + ); + + expect(resp.hits.hits[0]._source?.viewInAppUrl).contain('DISCOVER_APP_LOCATOR'); + expect(omit(parsedViewInAppUrl.params, 'timeRange.from')).eql({ + dataViewId: DATA_VIEW_ID, + dataViewSpec: DATA_VIEW_ID, + timeRange: { to: 'now' }, + query: { query: '', language: 'kuery' }, + filters: [], + }); + expect(parsedViewInAppUrl.params.timeRange.from).match(ISO_DATE_REGEX); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/index.ts index 45a8f2d8b1b40..ae9eb4a8d2419 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/alerting/custom_threshold/index.ts @@ -12,6 +12,7 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) loadTestFile(require.resolve('./avg_pct_fired')); loadTestFile(require.resolve('./avg_pct_no_data')); loadTestFile(require.resolve('./avg_ticks_fired')); + loadTestFile(require.resolve('./cardinality_runtime_field_fired')); loadTestFile(require.resolve('./custom_eq_avg_bytes_fired')); loadTestFile(require.resolve('./documents_count_fired')); loadTestFile(require.resolve('./group_by_fired')); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list.ts index c10d5181372b8..7d024a6e169c4 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list.ts @@ -21,15 +21,20 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { describe('API /api/metrics/process_list', () => { let supertestWithAdminScope: SupertestWithRoleScopeType; + before(async () => { supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', { withInternalHeaders: true, useCookieHeader: true, }); - await esArchiver.load('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); + await esArchiver.load( + 'x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes' + ); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); + await esArchiver.unload( + 'x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes' + ); await supertestWithAdminScope.destroy(); }); @@ -42,7 +47,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { 'host.name': 'gke-observability-8--observability-8--bc1afd95-nhhw', }, sourceId: 'default', - to: 1564432800000, + to: 1680027660000, sortBy: { name: 'cpu', isAscending: false, @@ -59,7 +64,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { const { processList, summary } = decodeOrThrow(ProcessListAPIResponseRT)(response.body); expect(processList.length).to.be(10); - expect(summary.total).to.be(178); + expect(summary.total).to.be(313); }); }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list_chart.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list_chart.ts index 10f214ea642f8..1761d5fb2969f 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list_chart.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/infra/metrics_process_list_chart.ts @@ -27,10 +27,14 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { withInternalHeaders: true, useCookieHeader: true, }); - await esArchiver.load('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); + await esArchiver.load( + 'x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes' + ); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/infra/8.0.0/metrics_and_apm'); + await esArchiver.unload( + 'x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes' + ); await supertestWithAdminScope.destroy(); }); @@ -43,8 +47,9 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { 'host.name': 'gke-observability-8--observability-8--bc1afd95-nhhw', }, indexPattern: 'metrics-*,metricbeat-*', - to: 1564432800000, - command: '/usr/lib/systemd/systemd-journald', + to: 1680027660000, + command: + '/System/Library/CoreServices/NotificationCenter.app/Contents/MacOS/NotificationCenter', }) ) .expect(200); diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/flush_config.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/flush_config.ts index 560e091c067ab..8f44fc64a71f8 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/flush_config.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/flush_config.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { - StreamUpsertRequest, + isGroupStreamDefinitionBase, StreamGetResponse, WiredStreamGetResponse, } from '@kbn/streams-schema'; @@ -17,124 +17,7 @@ import { createStreamsRepositoryAdminClient, } from './helpers/repository_client'; import { disableStreams, enableStreams, indexDocument } from './helpers/requests'; - -type StreamPutItem = Omit & { name: string }; - -const streams: StreamPutItem[] = [ - { - name: 'logs', - stream: { - ingest: { - lifecycle: { dsl: {} }, - processing: [], - wired: { - fields: { - '@timestamp': { - type: 'date', - }, - message: { - type: 'match_only_text', - }, - 'host.name': { - type: 'keyword', - }, - 'log.level': { - type: 'keyword', - }, - 'stream.name': { - type: 'keyword', - }, - }, - }, - routing: [ - { - destination: 'logs.test', - if: { - and: [ - { - field: 'numberfield', - operator: 'gt', - value: 15, - }, - ], - }, - }, - { - destination: 'logs.test2', - if: { - and: [ - { - field: 'field2', - operator: 'eq', - value: 'abc', - }, - ], - }, - }, - ], - }, - }, - }, - { - name: 'logs.test', - stream: { - ingest: { - lifecycle: { inherit: {} }, - routing: [], - processing: [], - wired: { - fields: { - numberfield: { - type: 'long', - }, - }, - }, - }, - }, - }, - { - name: 'logs.test2', - stream: { - ingest: { - lifecycle: { inherit: {} }, - processing: [ - { - grok: { - field: 'message', - patterns: ['%{NUMBER:numberfield}'], - if: { always: {} }, - }, - }, - ], - wired: { - fields: { - field2: { - type: 'keyword', - }, - }, - }, - routing: [], - }, - }, - }, - { - name: 'logs.deeply.nested.streamname', - stream: { - ingest: { - lifecycle: { inherit: {} }, - processing: [], - wired: { - fields: { - field2: { - type: 'keyword', - }, - }, - }, - routing: [], - }, - }, - }, -]; +import { createStreams } from './helpers/create_streams'; export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { const roleScopedSupertest = getService('roleScopedSupertest'); @@ -147,7 +30,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { before(async () => { apiClient = await createStreamsRepositoryAdminClient(roleScopedSupertest); await enableStreams(apiClient); - await createStreams(); + await createStreams(apiClient); await indexDocuments(); }); @@ -156,7 +39,8 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { }); it('checks whether deeply nested stream is created correctly', async () => { - function getChildNames(stream: StreamGetResponse['stream']) { + function getChildNames(stream: StreamGetResponse['stream']): string[] { + if (isGroupStreamDefinitionBase(stream)) return []; return stream.ingest.routing.map((r) => r.destination); } const logs = await apiClient.fetch('GET /api/streams/{id}', { @@ -224,23 +108,6 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { expect(logsTest2Response.hits.total).to.eql({ value: 1, relation: 'eq' }); }); - async function createStreams() { - for (const { name: streamId, ...stream } of streams) { - await apiClient - .fetch('PUT /api/streams/{id}', { - params: { - body: { - ...stream, - dashboards: [], - } as StreamUpsertRequest, - path: { id: streamId }, - }, - }) - .expect(200) - .then((response) => expect(response.body.acknowledged).to.eql(true)); - } - } - async function indexDocuments() { // send data that stays in logs const doc = { diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/group_streams.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/group_streams.ts new file mode 100644 index 0000000000000..fc9b74b314bab --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/group_streams.ts @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { DeploymentAgnosticFtrProviderContext } from '../../../ftr_provider_context'; +import { + StreamsSupertestRepositoryClient, + createStreamsRepositoryAdminClient, +} from './helpers/repository_client'; +import { disableStreams, enableStreams } from './helpers/requests'; +import { createStreams } from './helpers/create_streams'; + +export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { + const roleScopedSupertest = getService('roleScopedSupertest'); + + let apiClient: StreamsSupertestRepositoryClient; + + // An anticipated use case is that a user will want to flush a tree of streams from a config file + describe('GroupStreamDefinition', () => { + describe('CRUD API Operations', () => { + before(async () => { + apiClient = await createStreamsRepositoryAdminClient(roleScopedSupertest); + await enableStreams(apiClient); + await createStreams(apiClient); + }); + + after(async () => { + await disableStreams(apiClient); + }); + + it('successfully creates a GroupStream', async () => { + await apiClient + .fetch('PUT /api/streams/{id}', { + params: { + path: { id: 'test-group' }, + body: { + stream: { + group: { + members: ['logs', 'logs.test2'], + }, + }, + dashboards: [], + }, + }, + }) + .expect(200) + .then((response) => expect(response.body.acknowledged).to.eql(true)); + }); + + it('successfully creates a second GroupStream', async () => { + await apiClient + .fetch('PUT /api/streams/{id}', { + params: { + path: { id: 'test-group-too' }, + body: { + stream: { + group: { + members: ['logs.test2'], + }, + }, + dashboards: [], + }, + }, + }) + .expect(200) + .then((response) => expect(response.body.acknowledged).to.eql(true)); + }); + + it('unsuccessfully updates a GroupStream with an uknown stream', async () => { + await apiClient + .fetch('PUT /api/streams/{id}', { + params: { + path: { id: 'test-group' }, + body: { + stream: { + group: { + members: ['logs', 'non-existent-stream'], + }, + }, + dashboards: [], + }, + }, + }) + .expect(404); + }); + + it('unsuccessfully updates a GroupStream with an itself as a member', async () => { + await apiClient + .fetch('PUT /api/streams/{id}', { + params: { + path: { id: 'test-group' }, + body: { + stream: { + group: { + members: ['logs', 'test-group'], + }, + }, + dashboards: [], + }, + }, + }) + .expect(400); + }); + + it('unsuccessfully updates a GroupStream with a forbidden member', async () => { + await apiClient + .fetch('PUT /api/streams/{id}', { + params: { + path: { id: 'test-group' }, + body: { + stream: { + group: { + members: ['logs', 'test-group-too'], + }, + }, + dashboards: [], + }, + }, + }) + .expect(400); + }); + + it('successfully deletes a GroupStream', async () => { + await apiClient + .fetch('DELETE /api/streams/{id}', { + params: { + path: { id: 'test-group-too' }, + }, + }) + .expect(200); + }); + + it('successfully reads a GroupStream', async () => { + const response = await apiClient + .fetch('GET /api/streams/{id}', { + params: { + path: { id: 'test-group' }, + }, + }) + .expect(200); + expect(response.body).to.eql({ + stream: { + name: 'test-group', + group: { + members: ['logs', 'logs.test2'], + }, + }, + dashboards: [], + }); + }); + + it('successfully lists a GroupStream', async () => { + const response = await apiClient.fetch('GET /api/streams').expect(200); + expect(response.body.streams.some((stream) => stream.name === 'test-group')).to.eql(true); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/helpers/create_streams.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/helpers/create_streams.ts new file mode 100644 index 0000000000000..bba31350b4145 --- /dev/null +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/helpers/create_streams.ts @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { StreamUpsertRequest } from '@kbn/streams-schema'; +import expect from '@kbn/expect'; +import { StreamsSupertestRepositoryClient } from './repository_client'; + +type StreamPutItem = Omit & { name: string }; + +const streams: StreamPutItem[] = [ + { + name: 'logs', + stream: { + ingest: { + lifecycle: { dsl: {} }, + processing: [], + wired: { + fields: { + '@timestamp': { + type: 'date', + }, + message: { + type: 'match_only_text', + }, + 'host.name': { + type: 'keyword', + }, + 'log.level': { + type: 'keyword', + }, + 'stream.name': { + type: 'keyword', + }, + }, + }, + routing: [ + { + destination: 'logs.test', + if: { + and: [ + { + field: 'numberfield', + operator: 'gt', + value: 15, + }, + ], + }, + }, + { + destination: 'logs.test2', + if: { + and: [ + { + field: 'field2', + operator: 'eq', + value: 'abc', + }, + ], + }, + }, + ], + }, + }, + }, + { + name: 'logs.test', + stream: { + ingest: { + lifecycle: { inherit: {} }, + routing: [], + processing: [], + wired: { + fields: { + numberfield: { + type: 'long', + }, + }, + }, + }, + }, + }, + { + name: 'logs.test2', + stream: { + ingest: { + lifecycle: { inherit: {} }, + processing: [ + { + grok: { + field: 'message', + patterns: ['%{NUMBER:numberfield}'], + if: { always: {} }, + }, + }, + ], + wired: { + fields: { + field2: { + type: 'keyword', + }, + }, + }, + routing: [], + }, + }, + }, + { + name: 'logs.deeply.nested.streamname', + stream: { + ingest: { + lifecycle: { inherit: {} }, + processing: [], + wired: { + fields: { + field2: { + type: 'keyword', + }, + }, + }, + routing: [], + }, + }, + }, +]; + +export async function createStreams(apiClient: StreamsSupertestRepositoryClient) { + for (const { name: streamId, ...stream } of streams) { + await apiClient + .fetch('PUT /api/streams/{id}', { + params: { + body: { + ...stream, + dashboards: [], + } as StreamUpsertRequest, + path: { id: streamId }, + }, + }) + .expect(200) + .then((response) => expect(response.body.acknowledged).to.eql(true)); + } +} diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/index.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/index.ts index aa3861501bc21..00cb65111cb7c 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/index.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/index.ts @@ -17,6 +17,7 @@ export default function ({ loadTestFile }: DeploymentAgnosticFtrProviderContext) loadTestFile(require.resolve('./schema')); loadTestFile(require.resolve('./processing_simulate')); loadTestFile(require.resolve('./root_stream')); + loadTestFile(require.resolve('./group_streams')); loadTestFile(require.resolve('./lifecycle')); }); } diff --git a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/lifecycle.ts b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/lifecycle.ts index 64d7b2e0949c2..73152116931e1 100644 --- a/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/lifecycle.ts +++ b/x-pack/test/api_integration/deployment_agnostic/apis/observability/streams/lifecycle.ts @@ -11,6 +11,7 @@ import { IngestStreamLifecycle, IngestStreamUpsertRequest, WiredStreamGetResponse, + asIngestStreamGetResponse, isDslLifecycle, isIlmLifecycle, } from '@kbn/streams-schema'; @@ -34,7 +35,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { ) { const definitions = await Promise.all(streams.map((stream) => getStream(apiClient, stream))); for (const definition of definitions) { - expect(definition.effective_lifecycle).to.eql(expectedLifecycle); + expect(asIngestStreamGetResponse(definition).effective_lifecycle).to.eql(expectedLifecycle); } const dataStreams = await esClient.indices.getDataStream({ name: streams }); @@ -105,7 +106,7 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) { expect((updatedRootDefinition as WiredStreamGetResponse).stream.ingest.lifecycle).to.eql({ dsl: { data_retention: '999d' }, }); - expect(updatedRootDefinition.effective_lifecycle).to.eql({ + expect((updatedRootDefinition as WiredStreamGetResponse).effective_lifecycle).to.eql({ dsl: { data_retention: '999d' }, from: 'logs', }); diff --git a/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts b/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts index 6b03bdf46b273..6f3c38b872f19 100644 --- a/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts +++ b/x-pack/test/api_integration/deployment_agnostic/services/data_view_api.ts @@ -19,12 +19,14 @@ export function DataViewApiProvider({ getService }: DeploymentAgnosticFtrProvide name, title, spaceId, + data, }: { roleAuthc: RoleCredentials; id: string; name: string; title: string; spaceId?: string; + data?: Record; }) { const { body } = await supertestWithoutAuth .post(`${spaceId ? '/s/' + spaceId : ''}/api/content_management/rpc/create`) @@ -43,6 +45,7 @@ export function DataViewApiProvider({ getService }: DeploymentAgnosticFtrProvide typeMeta: '{}', runtimeFieldMap: '{}', name, + ...(data ? data : {}), }, options: { id }, version: 1, diff --git a/x-pack/test/api_integration/services/security_solution_api.gen.ts b/x-pack/test/api_integration/services/security_solution_api.gen.ts index a069b2e1134ce..b3b0eb4571674 100644 --- a/x-pack/test/api_integration/services/security_solution_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_api.gen.ts @@ -1325,7 +1325,7 @@ finalize it. kibanaSpace: string = 'default' ) { return supertest - .post(routeWithNamespace('/api/detection_engine/signals/migration_status', kibanaSpace)) + .get(routeWithNamespace('/api/detection_engine/signals/migration_status', kibanaSpace)) .set('kbn-xsrf', 'true') .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') diff --git a/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts b/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts index 73459c7f6ddb0..b8d1dda4a9ebe 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/request_diagnostics.ts @@ -122,7 +122,7 @@ export default function (providerContext: FtrProviderContext) { clearInterval(intervalId); resolve({}); } - }, 1000); + }, 3000); }).catch((e) => { throw e; }); diff --git a/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts b/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts index bf0843d97d011..bec1e489aedc2 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/update_agent_tags.ts @@ -38,7 +38,7 @@ export async function pollResult( await verifyActionResult(); resolve({}); } - }, 1000); + }, 3000); }).catch((e) => { throw e; }); diff --git a/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts b/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts index 40e349458f330..cae40fd71aa9c 100644 --- a/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts +++ b/x-pack/test/fleet_api_integration/apis/space_awareness/change_space_agent_policies.ts @@ -58,6 +58,7 @@ export default function (providerContext: FtrProviderContext) { const spaces = getService('spaces'); let TEST_SPACE_1: string; + // Failing: See https://github.com/elastic/kibana/issues/209008 // Failing: See https://github.com/elastic/kibana/issues/209008 describe.skip('change space agent policies', function () { skipIfNoDockerRegistry(providerContext); diff --git a/x-pack/test/functional/apps/dataset_quality/dataset_quality_details.ts b/x-pack/test/functional/apps/dataset_quality/dataset_quality_details.ts index 043e116f33e8a..c1d60d59d3029 100644 --- a/x-pack/test/functional/apps/dataset_quality/dataset_quality_details.ts +++ b/x-pack/test/functional/apps/dataset_quality/dataset_quality_details.ts @@ -318,22 +318,22 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid }); describe('navigation', () => { - it('should go to log explorer page when the open in log explorer button is clicked', async () => { + it('should go to discover page when the open in discover button is clicked', async () => { await PageObjects.datasetQuality.navigateToDetails({ dataStream: regularDataStreamName, }); - const logExplorerButton = + const discoverButton = await PageObjects.datasetQuality.getDatasetQualityDetailsHeaderButton(); - await logExplorerButton.click(); + await discoverButton.click(); // Confirm dataset selector text in observability logs explorer const datasetSelectorText = await PageObjects.discover.getCurrentDataViewId(); originalExpect(datasetSelectorText).toMatch(regularDatasetName); }); - it('should go log explorer for degraded docs when the button next to breakdown selector is clicked', async () => { + it('should go discover for degraded docs when the button next to breakdown selector is clicked', async () => { await PageObjects.datasetQuality.navigateToDetails({ dataStream: apacheAccessDataStreamName, }); diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts index 6e4d54d5f8217..f583552046b5a 100644 --- a/x-pack/test/functional/apps/infra/node_details.ts +++ b/x-pack/test/functional/apps/infra/node_details.ts @@ -262,6 +262,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'network', chartsCount: 1 }, ].forEach(({ metric, chartsCount }) => { it(`should render ${chartsCount} ${metric} chart(s) in the Metrics section`, async () => { + await waitForChartsToLoad(); const hosts = await pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric); expect(hosts.length).to.equal(chartsCount); }); @@ -488,8 +489,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); }); - - describe('Processes Tab', () => { + // FLAKY: https://github.com/elastic/kibana/issues/192891 + describe.skip('Processes Tab', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_hosts_processes'); await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); @@ -559,7 +560,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); }); - it('should render logs tab', async () => { + it('should render logs tab content', async () => { await pageObjects.assetDetails.logsExists(); }); @@ -584,7 +585,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.assetDetails.clickOsqueryTab(); }); - it('should show a date picker', async () => { + it('should not show a date picker', async () => { expect(await pageObjects.timePicker.timePickerExists()).to.be(false); }); }); @@ -736,6 +737,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ].forEach(({ metric, chartsCount }) => { it(`should render ${chartsCount} ${metric} chart`, async () => { await retry.tryForTime(5000, async () => { + await waitForChartsToLoad(); const charts = await (metric === 'kubernetes' ? pageObjects.assetDetails.getOverviewTabKubernetesMetricCharts() : pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric)); diff --git a/x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes/data.json.gz b/x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes/data.json.gz new file mode 100644 index 0000000000000..284117e881326 Binary files /dev/null and b/x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes/mappings.json b/x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes/mappings.json new file mode 100644 index 0000000000000..5e69d67d1e561 --- /dev/null +++ b/x-pack/test/functional/es_archives/infra/8.0.0/metrics_hosts_processes/mappings.json @@ -0,0 +1,26342 @@ +{ + "type": "index", + "value": { + "aliases": { + }, + "index": "metricbeat-8.5.0", + "mappings": { + "_meta": { + "beat": "metricbeat", + "version": "8.5.0" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "*", + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "*", + "path_match": "kubernetes.annotations.*" + } + }, + { + "kubernetes.selectors.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "*", + "path_match": "kubernetes.selectors.*" + } + }, + { + "docker.cpu.core.*.pct": { + "mapping": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "match_mapping_type": "*", + "path_match": "docker.cpu.core.*.pct" + } + }, + { + "docker.cpu.core.*.norm.pct": { + "mapping": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "match_mapping_type": "*", + "path_match": "docker.cpu.core.*.norm.pct" + } + }, + { + "docker.cpu.core.*.ticks": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "docker.cpu.core.*.ticks" + } + }, + { + "docker.event.actor.attributes": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.event.actor.attributes.*" + } + }, + { + "docker.image.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.image.labels.*" + } + }, + { + "docker.memory.stats.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "*", + "path_match": "docker.memory.stats.*" + } + }, + { + "etcd.disk.wal_fsync_duration.ns.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "etcd.disk.wal_fsync_duration.ns.bucket.*" + } + }, + { + "etcd.disk.backend_commit_duration.ns.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "etcd.disk.backend_commit_duration.ns.bucket.*" + } + }, + { + "kubernetes.apiserver.watch.events.size.bytes.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.apiserver.watch.events.size.bytes.bucket.*" + } + }, + { + "kubernetes.apiserver.response.size.bytes.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.apiserver.response.size.bytes.bucket.*" + } + }, + { + "kubernetes.apiserver.request.duration.us.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.apiserver.request.duration.us.bucket.*" + } + }, + { + "kubernetes.controllermanager.client.request.duration.us.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.controllermanager.client.request.duration.us.bucket.*" + } + }, + { + "kubernetes.proxy.http.request.duration.us.percentile.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "double", + "path_match": "kubernetes.proxy.http.request.duration.us.percentile.*" + } + }, + { + "kubernetes.proxy.http.request.size.bytes.percentile.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.proxy.http.request.size.bytes.percentile.*" + } + }, + { + "kubernetes.proxy.http.response.size.bytes.percentile.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.proxy.http.response.size.bytes.percentile.*" + } + }, + { + "kubernetes.proxy.sync.rules.duration.us.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.proxy.sync.rules.duration.us.bucket.*" + } + }, + { + "kubernetes.proxy.sync.networkprogramming.duration.us.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.proxy.sync.networkprogramming.duration.us.bucket.*" + } + }, + { + "kubernetes.scheduler.http.request.duration.us.percentile.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "double", + "path_match": "kubernetes.scheduler.http.request.duration.us.percentile.*" + } + }, + { + "kubernetes.scheduler.http.request.size.bytes.percentile.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.scheduler.http.request.size.bytes.percentile.*" + } + }, + { + "kubernetes.scheduler.http.response.size.bytes.percentile.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.scheduler.http.response.size.bytes.percentile.*" + } + }, + { + "kubernetes.scheduler.scheduling.e2e.duration.us.bucket.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "kubernetes.scheduler.scheduling.e2e.duration.us.bucket.*" + } + }, + { + "kubernetes.scheduler.scheduling.duration.seconds.percentile.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "double", + "path_match": "kubernetes.scheduler.scheduling.duration.seconds.percentile.*" + } + }, + { + "munin.metrics.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "*", + "path_match": "munin.metrics.*" + } + }, + { + "openmetrics.labels.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "openmetrics.labels.*" + } + }, + { + "openmetrics.metrics.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "*", + "path_match": "openmetrics.metrics.*" + } + }, + { + "openmetrics.exemplar.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "openmetrics.exemplar.*" + } + }, + { + "openmetrics.exemplar.labels.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "openmetrics.exemplar.labels.*" + } + }, + { + "prometheus.labels.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "prometheus.labels.*" + } + }, + { + "prometheus.metrics.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "*", + "path_match": "prometheus.metrics.*" + } + }, + { + "prometheus.query.*": { + "mapping": { + "type": "double" + }, + "match_mapping_type": "*", + "path_match": "prometheus.query.*" + } + }, + { + "system.process.env": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "system.process.env.*" + } + }, + { + "system.process.cgroup.cpuacct.percpu": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "system.process.cgroup.cpuacct.percpu.*" + } + }, + { + "system.raid.disks.states.*": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "system.raid.disks.states.*" + } + }, + { + "traefik.health.response.status_codes.*": { + "mapping": { + "type": "long" + }, + "match_mapping_type": "long", + "path_match": "traefik.health.response.status_codes.*" + } + }, + { + "vsphere.virtualmachine.custom_fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "vsphere.virtualmachine.custom_fields.*" + } + }, + { + "windows.perfmon.metrics.*.*": { + "mapping": { + "type": "float" + }, + "match_mapping_type": "*", + "path_match": "windows.perfmon.metrics.*.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "aerospike": { + "properties": { + "namespace": { + "properties": { + "client": { + "properties": { + "delete": { + "properties": { + "error": { + "type": "long" + }, + "not_found": { + "type": "long" + }, + "success": { + "type": "long" + }, + "timeout": { + "type": "long" + } + } + }, + "read": { + "properties": { + "error": { + "type": "long" + }, + "not_found": { + "type": "long" + }, + "success": { + "type": "long" + }, + "timeout": { + "type": "long" + } + } + }, + "write": { + "properties": { + "error": { + "type": "long" + }, + "success": { + "type": "long" + }, + "timeout": { + "type": "long" + } + } + } + } + }, + "device": { + "properties": { + "available": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "free": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "hwm_breached": { + "type": "boolean" + }, + "memory": { + "properties": { + "free": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "used": { + "properties": { + "data": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "index": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "sindex": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "objects": { + "properties": { + "master": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "stop_writes": { + "type": "boolean" + } + } + } + } + }, + "agent": { + "properties": { + "build": { + "properties": { + "original": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "path": "agent.name", + "type": "alias" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "apache": { + "properties": { + "status": { + "properties": { + "bytes_per_request": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "bytes_per_sec": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "connections": { + "properties": { + "async": { + "properties": { + "closing": { + "type": "long" + }, + "keep_alive": { + "type": "long" + }, + "writing": { + "type": "long" + } + } + }, + "total": { + "type": "long" + } + } + }, + "cpu": { + "properties": { + "children_system": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "children_user": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "load": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "system": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "user": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "load": { + "properties": { + "1": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "15": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "5": { + "scaling_factor": 100, + "type": "scaled_float" + } + } + }, + "requests_per_sec": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "scoreboard": { + "properties": { + "closing_connection": { + "type": "long" + }, + "dns_lookup": { + "type": "long" + }, + "gracefully_finishing": { + "type": "long" + }, + "idle_cleanup": { + "type": "long" + }, + "keepalive": { + "type": "long" + }, + "logging": { + "type": "long" + }, + "open_slot": { + "type": "long" + }, + "reading_request": { + "type": "long" + }, + "sending_reply": { + "type": "long" + }, + "starting_up": { + "type": "long" + }, + "total": { + "type": "long" + }, + "waiting_for_connection": { + "type": "long" + } + } + }, + "total_accesses": { + "type": "long" + }, + "total_kbytes": { + "type": "long" + }, + "uptime": { + "properties": { + "server_uptime": { + "type": "long" + }, + "uptime": { + "type": "long" + } + } + }, + "workers": { + "properties": { + "busy": { + "type": "long" + }, + "idle": { + "type": "long" + } + } + } + } + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "beat": { + "properties": { + "elasticsearch": { + "properties": { + "cluster": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "properties": { + "beat": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cluster": { + "properties": { + "uuid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "containerized": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "input": { + "properties": { + "count": { + "type": "long" + }, + "names": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "management": { + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "module": { + "properties": { + "count": { + "type": "long" + }, + "names": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "output": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "queue": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "service": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "stats": { + "properties": { + "apm_server": { + "properties": { + "acm": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + } + } + }, + "response": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "properties": { + "closed": { + "type": "long" + }, + "count": { + "type": "long" + }, + "decode": { + "type": "long" + }, + "forbidden": { + "type": "long" + }, + "internal": { + "type": "long" + }, + "invalidquery": { + "type": "long" + }, + "method": { + "type": "long" + }, + "notfound": { + "type": "long" + }, + "queue": { + "type": "long" + }, + "ratelimit": { + "type": "long" + }, + "toolarge": { + "type": "long" + }, + "unauthorized": { + "type": "long" + }, + "unavailable": { + "type": "long" + }, + "validate": { + "type": "long" + } + } + }, + "request": { + "properties": { + "count": { + "type": "long" + } + } + }, + "unset": { + "type": "long" + }, + "valid": { + "properties": { + "accepted": { + "type": "long" + }, + "count": { + "type": "long" + }, + "notmodified": { + "type": "long" + }, + "ok": { + "type": "long" + } + } + } + } + } + } + }, + "decoder": { + "properties": { + "deflate": { + "properties": { + "content-length": { + "type": "long" + }, + "count": { + "type": "long" + } + } + }, + "gzip": { + "properties": { + "content-length": { + "type": "long" + }, + "count": { + "type": "long" + } + } + }, + "missing-content-length": { + "properties": { + "count": { + "type": "long" + } + } + }, + "reader": { + "properties": { + "count": { + "type": "long" + }, + "size": { + "type": "long" + } + } + }, + "uncompressed": { + "properties": { + "content-length": { + "type": "long" + }, + "count": { + "type": "long" + } + } + } + } + }, + "processor": { + "properties": { + "error": { + "properties": { + "decoding": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + }, + "frames": { + "type": "long" + }, + "spans": { + "type": "long" + }, + "stacktraces": { + "type": "long" + }, + "transformations": { + "type": "long" + }, + "validation": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + } + } + }, + "metric": { + "properties": { + "decoding": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + }, + "transformations": { + "type": "long" + }, + "validation": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + } + } + }, + "sourcemap": { + "properties": { + "counter": { + "type": "long" + }, + "decoding": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + }, + "validation": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + } + } + }, + "span": { + "properties": { + "transformations": { + "type": "long" + } + } + }, + "transaction": { + "properties": { + "decoding": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + }, + "frames": { + "type": "long" + }, + "spans": { + "type": "long" + }, + "stacktraces": { + "type": "long" + }, + "transactions": { + "type": "long" + }, + "transformations": { + "type": "long" + }, + "validation": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + } + } + } + } + }, + "server": { + "properties": { + "concurrent": { + "properties": { + "wait": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "request": { + "properties": { + "count": { + "type": "long" + } + } + }, + "response": { + "properties": { + "count": { + "type": "long" + }, + "errors": { + "properties": { + "closed": { + "type": "long" + }, + "concurrency": { + "type": "long" + }, + "count": { + "type": "long" + }, + "decode": { + "type": "long" + }, + "forbidden": { + "type": "long" + }, + "internal": { + "type": "long" + }, + "method": { + "type": "long" + }, + "queue": { + "type": "long" + }, + "ratelimit": { + "type": "long" + }, + "toolarge": { + "type": "long" + }, + "unauthorized": { + "type": "long" + }, + "validate": { + "type": "long" + } + } + }, + "valid": { + "properties": { + "accepted": { + "type": "long" + }, + "count": { + "type": "long" + }, + "ok": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "beat": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cgroup": { + "properties": { + "cpu": { + "properties": { + "cfs": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } + } + }, + "quota": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "stats": { + "properties": { + "periods": { + "type": "long" + }, + "throttled": { + "properties": { + "ns": { + "type": "long" + }, + "periods": { + "type": "long" + } + } + } + } + } + } + }, + "cpuacct": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "total": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "mem": { + "properties": { + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "cpu": { + "properties": { + "system": { + "properties": { + "ticks": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "total": { + "properties": { + "ticks": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "value": { + "type": "long" + } + } + }, + "user": { + "properties": { + "ticks": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "handles": { + "properties": { + "limit": { + "properties": { + "hard": { + "type": "long" + }, + "soft": { + "type": "long" + } + } + }, + "open": { + "type": "long" + } + } + }, + "info": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "libbeat": { + "properties": { + "config": { + "properties": { + "reloads": { + "type": "short" + }, + "running": { + "type": "short" + }, + "starts": { + "type": "short" + }, + "stops": { + "type": "short" + } + } + }, + "output": { + "properties": { + "events": { + "properties": { + "acked": { + "type": "long" + }, + "active": { + "type": "long" + }, + "batches": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "duplicates": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "toomany": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "read": { + "properties": { + "bytes": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "write": { + "properties": { + "bytes": { + "type": "long" + }, + "errors": { + "type": "long" + } + } + } + } + }, + "pipeline": { + "properties": { + "clients": { + "type": "long" + }, + "events": { + "properties": { + "active": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "filtered": { + "type": "long" + }, + "published": { + "type": "long" + }, + "retry": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "queue": { + "properties": { + "acked": { + "type": "long" + } + } + } + } + } + } + }, + "memstats": { + "properties": { + "gc_next": { + "type": "long" + }, + "memory": { + "properties": { + "alloc": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "rss": { + "type": "long" + } + } + }, + "runtime": { + "properties": { + "goroutines": { + "type": "long" + } + } + }, + "system": { + "properties": { + "cpu": { + "properties": { + "cores": { + "type": "long" + } + } + }, + "load": { + "properties": { + "1": { + "type": "double" + }, + "15": { + "type": "double" + }, + "5": { + "type": "double" + }, + "norm": { + "properties": { + "1": { + "type": "double" + }, + "15": { + "type": "double" + }, + "5": { + "type": "double" + } + } + } + } + } + } + }, + "uptime": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "beats_state": { + "properties": { + "beat": { + "properties": { + "host": { + "path": "beat.state.beat.host", + "type": "alias" + }, + "name": { + "path": "beat.state.beat.name", + "type": "alias" + }, + "type": { + "path": "beat.state.beat.type", + "type": "alias" + }, + "uuid": { + "path": "beat.state.beat.uuid", + "type": "alias" + }, + "version": { + "path": "beat.state.beat.version", + "type": "alias" + } + } + }, + "state": { + "properties": { + "beat": { + "properties": { + "name": { + "path": "beat.state.beat.name", + "type": "alias" + } + } + }, + "host": { + "properties": { + "architecture": { + "path": "host.architecture", + "type": "alias" + }, + "hostname": { + "path": "host.hostname", + "type": "alias" + }, + "name": { + "path": "host.name", + "type": "alias" + }, + "os": { + "properties": { + "platform": { + "path": "beat.state.host.os.platform", + "type": "alias" + }, + "version": { + "path": "beat.state.host.os.version", + "type": "alias" + } + } + } + } + }, + "input": { + "properties": { + "count": { + "path": "beat.state.input.count", + "type": "alias" + }, + "names": { + "path": "beat.state.input.names", + "type": "alias" + } + } + }, + "module": { + "properties": { + "count": { + "path": "beat.state.module.count", + "type": "alias" + }, + "names": { + "path": "beat.state.module.names", + "type": "alias" + } + } + }, + "output": { + "properties": { + "name": { + "path": "beat.state.output.name", + "type": "alias" + } + } + }, + "service": { + "properties": { + "id": { + "path": "beat.state.service.id", + "type": "alias" + }, + "name": { + "path": "beat.state.service.name", + "type": "alias" + }, + "version": { + "path": "beat.state.service.version", + "type": "alias" + } + } + } + } + }, + "timestamp": { + "path": "@timestamp", + "type": "alias" + } + } + }, + "beats_stats": { + "properties": { + "apm-server": { + "properties": { + "acm": { + "properties": { + "request": { + "properties": { + "count": { + "path": "beat.stats.apm_server.acm.request.count", + "type": "alias" + } + } + }, + "response": { + "properties": { + "count": { + "path": "beat.stats.apm_server.acm.response.count", + "type": "alias" + }, + "errors": { + "properties": { + "closed": { + "path": "beat.stats.apm_server.acm.response.errors.closed", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.acm.response.errors.count", + "type": "alias" + }, + "decode": { + "path": "beat.stats.apm_server.acm.response.errors.decode", + "type": "alias" + }, + "forbidden": { + "path": "beat.stats.apm_server.acm.response.errors.forbidden", + "type": "alias" + }, + "internal": { + "path": "beat.stats.apm_server.acm.response.errors.internal", + "type": "alias" + }, + "invalidquery": { + "path": "beat.stats.apm_server.acm.response.errors.invalidquery", + "type": "alias" + }, + "method": { + "path": "beat.stats.apm_server.acm.response.errors.method", + "type": "alias" + }, + "notfound": { + "path": "beat.stats.apm_server.acm.response.errors.notfound", + "type": "alias" + }, + "queue": { + "path": "beat.stats.apm_server.acm.response.errors.queue", + "type": "alias" + }, + "ratelimit": { + "path": "beat.stats.apm_server.acm.response.errors.ratelimit", + "type": "alias" + }, + "toolarge": { + "path": "beat.stats.apm_server.acm.response.errors.toolarge", + "type": "alias" + }, + "unauthorized": { + "path": "beat.stats.apm_server.acm.response.errors.unauthorized", + "type": "alias" + }, + "unavailable": { + "path": "beat.stats.apm_server.acm.response.errors.unavailable", + "type": "alias" + }, + "validate": { + "path": "beat.stats.apm_server.acm.response.errors.validate", + "type": "alias" + } + } + }, + "request": { + "properties": { + "count": { + "path": "beat.stats.apm_server.acm.response.request.count", + "type": "alias" + } + } + }, + "unset": { + "path": "beat.stats.apm_server.acm.response.unset", + "type": "alias" + }, + "valid": { + "properties": { + "accepted": { + "path": "beat.stats.apm_server.acm.response.valid.accepted", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.acm.response.valid.count", + "type": "alias" + }, + "notmodified": { + "path": "beat.stats.apm_server.acm.response.valid.notmodified", + "type": "alias" + }, + "ok": { + "path": "beat.stats.apm_server.acm.response.valid.ok", + "type": "alias" + } + } + } + } + } + } + }, + "decoder": { + "properties": { + "deflate": { + "properties": { + "content-length": { + "path": "beat.stats.apm_server.decoder.deflate.content-length", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.decoder.deflate.count", + "type": "alias" + } + } + }, + "gzip": { + "properties": { + "content-length": { + "path": "beat.stats.apm_server.decoder.gzip.content-length", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.decoder.gzip.count", + "type": "alias" + } + } + }, + "missing-content-length": { + "properties": { + "count": { + "path": "beat.stats.apm_server.decoder.missing-content-length.count", + "type": "alias" + } + } + }, + "reader": { + "properties": { + "count": { + "path": "beat.stats.apm_server.decoder.reader.count", + "type": "alias" + }, + "size": { + "path": "beat.stats.apm_server.decoder.reader.size", + "type": "alias" + } + } + }, + "uncompressed": { + "properties": { + "content-length": { + "path": "beat.stats.apm_server.decoder.uncompressed.content-length", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.decoder.uncompressed.count", + "type": "alias" + } + } + } + } + }, + "processor": { + "properties": { + "error": { + "properties": { + "decoding": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.error.decoding.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.error.decoding.errors", + "type": "alias" + } + } + }, + "frames": { + "path": "beat.stats.apm_server.processor.error.frames", + "type": "alias" + }, + "spans": { + "path": "beat.stats.apm_server.processor.error.spans", + "type": "alias" + }, + "stacktraces": { + "path": "beat.stats.apm_server.processor.error.stacktraces", + "type": "alias" + }, + "transformations": { + "path": "beat.stats.apm_server.processor.error.transformations", + "type": "alias" + }, + "validation": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.error.validation.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.error.validation.errors", + "type": "alias" + } + } + } + } + }, + "metric": { + "properties": { + "decoding": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.metric.decoding.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.metric.decoding.errors", + "type": "alias" + } + } + }, + "transformations": { + "path": "beat.stats.apm_server.processor.metric.transformations", + "type": "alias" + }, + "validation": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.metric.validation.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.metric.validation.errors", + "type": "alias" + } + } + } + } + }, + "sourcemap": { + "properties": { + "counter": { + "path": "beat.stats.apm_server.processor.sourcemap.counter", + "type": "alias" + }, + "decoding": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.sourcemap.decoding.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.sourcemap.decoding.errors", + "type": "alias" + } + } + }, + "validation": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.sourcemap.validation.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.sourcemap.validation.errors", + "type": "alias" + } + } + } + } + }, + "span": { + "properties": { + "transformations": { + "path": "beat.stats.apm_server.processor.span.transformations", + "type": "alias" + } + } + }, + "transaction": { + "properties": { + "decoding": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.transaction.decoding.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.transaction.decoding.errors", + "type": "alias" + } + } + }, + "frames": { + "path": "beat.stats.apm_server.processor.transaction.frames", + "type": "alias" + }, + "spans": { + "path": "beat.stats.apm_server.processor.transaction.spans", + "type": "alias" + }, + "stacktraces": { + "path": "beat.stats.apm_server.processor.transaction.stacktraces", + "type": "alias" + }, + "transactions": { + "path": "beat.stats.apm_server.processor.transaction.transactions", + "type": "alias" + }, + "transformations": { + "path": "beat.stats.apm_server.processor.transaction.transformations", + "type": "alias" + }, + "validation": { + "properties": { + "count": { + "path": "beat.stats.apm_server.processor.transaction.validation.count", + "type": "alias" + }, + "errors": { + "path": "beat.stats.apm_server.processor.transaction.validation.errors", + "type": "alias" + } + } + } + } + } + } + }, + "server": { + "properties": { + "concurrent": { + "properties": { + "wait": { + "properties": { + "ms": { + "path": "beat.stats.apm_server.server.concurrent.wait.ms", + "type": "alias" + } + } + } + } + }, + "request": { + "properties": { + "count": { + "path": "beat.stats.apm_server.server.request.count", + "type": "alias" + } + } + }, + "response": { + "properties": { + "count": { + "path": "beat.stats.apm_server.server.response.count", + "type": "alias" + }, + "errors": { + "properties": { + "closed": { + "path": "beat.stats.apm_server.server.response.errors.closed", + "type": "alias" + }, + "concurrency": { + "path": "beat.stats.apm_server.server.response.errors.concurrency", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.server.response.errors.count", + "type": "alias" + }, + "decode": { + "path": "beat.stats.apm_server.server.response.errors.decode", + "type": "alias" + }, + "forbidden": { + "path": "beat.stats.apm_server.server.response.errors.forbidden", + "type": "alias" + }, + "internal": { + "path": "beat.stats.apm_server.server.response.errors.internal", + "type": "alias" + }, + "method": { + "path": "beat.stats.apm_server.server.response.errors.method", + "type": "alias" + }, + "queue": { + "path": "beat.stats.apm_server.server.response.errors.queue", + "type": "alias" + }, + "ratelimit": { + "path": "beat.stats.apm_server.server.response.errors.ratelimit", + "type": "alias" + }, + "toolarge": { + "path": "beat.stats.apm_server.server.response.errors.toolarge", + "type": "alias" + }, + "unauthorized": { + "path": "beat.stats.apm_server.server.response.errors.unauthorized", + "type": "alias" + }, + "validate": { + "path": "beat.stats.apm_server.server.response.errors.validate", + "type": "alias" + } + } + }, + "valid": { + "properties": { + "accepted": { + "path": "beat.stats.apm_server.server.response.valid.accepted", + "type": "alias" + }, + "count": { + "path": "beat.stats.apm_server.server.response.valid.count", + "type": "alias" + }, + "ok": { + "path": "beat.stats.apm_server.server.response.valid.ok", + "type": "alias" + } + } + } + } + } + } + } + } + }, + "beat": { + "properties": { + "host": { + "path": "beat.stats.info.host", + "type": "alias" + }, + "name": { + "path": "beat.stats.info.name", + "type": "alias" + }, + "type": { + "path": "beat.stats.info.type", + "type": "alias" + }, + "uuid": { + "path": "beat.stats.info.uuid", + "type": "alias" + }, + "version": { + "path": "beat.stats.info.version", + "type": "alias" + } + } + }, + "metrics": { + "properties": { + "beat": { + "properties": { + "cgroup": { + "properties": { + "cpu": { + "properties": { + "cfs": { + "properties": { + "period": { + "properties": { + "us": { + "path": "beat.stats.cgroup.cpu.cfs.period.us", + "type": "alias" + } + } + }, + "quota": { + "properties": { + "us": { + "path": "beat.stats.cgroup.cpu.cfs.quota.us", + "type": "alias" + } + } + } + } + }, + "id": { + "path": "beat.stats.cgroup.cpu.id", + "type": "alias" + }, + "stats": { + "properties": { + "periods": { + "path": "beat.stats.cgroup.cpu.stats.periods", + "type": "alias" + }, + "throttled": { + "properties": { + "ns": { + "path": "beat.stats.cgroup.cpu.stats.throttled.ns", + "type": "alias" + }, + "periods": { + "path": "beat.stats.cgroup.cpu.stats.throttled.periods", + "type": "alias" + } + } + } + } + } + } + }, + "cpuacct": { + "properties": { + "id": { + "path": "beat.stats.cgroup.cpuacct.id", + "type": "alias" + }, + "total": { + "properties": { + "ns": { + "path": "beat.stats.cgroup.cpuacct.total.ns", + "type": "alias" + } + } + } + } + }, + "mem": { + "properties": { + "limit": { + "properties": { + "bytes": { + "path": "beat.stats.cgroup.memory.mem.limit.bytes", + "type": "alias" + } + } + }, + "usage": { + "properties": { + "bytes": { + "path": "beat.stats.cgroup.memory.mem.usage.bytes", + "type": "alias" + } + } + } + } + }, + "memory": { + "properties": { + "id": { + "path": "beat.stats.cgroup.memory.id", + "type": "alias" + } + } + } + } + }, + "cpu": { + "properties": { + "system": { + "properties": { + "ticks": { + "path": "beat.stats.cpu.system.ticks", + "type": "alias" + }, + "time": { + "properties": { + "ms": { + "path": "beat.stats.cpu.system.time.ms", + "type": "alias" + } + } + } + } + }, + "total": { + "properties": { + "ticks": { + "path": "beat.stats.cpu.total.ticks", + "type": "alias" + }, + "time": { + "properties": { + "ms": { + "path": "beat.stats.cpu.total.time.ms", + "type": "alias" + } + } + }, + "value": { + "path": "beat.stats.cpu.total.value", + "type": "alias" + } + } + }, + "user": { + "properties": { + "ticks": { + "path": "beat.stats.cpu.user.ticks", + "type": "alias" + }, + "time": { + "properties": { + "ms": { + "path": "beat.stats.cpu.user.time.ms", + "type": "alias" + } + } + } + } + } + } + }, + "handles": { + "properties": { + "limit": { + "properties": { + "hard": { + "path": "beat.stats.handles.limit.hard", + "type": "alias" + }, + "soft": { + "path": "beat.stats.handles.limit.soft", + "type": "alias" + } + } + }, + "open": { + "path": "beat.stats.handles.open", + "type": "alias" + } + } + }, + "info": { + "properties": { + "ephemeral_id": { + "path": "beat.stats.info.ephemeral_id", + "type": "alias" + }, + "uptime": { + "properties": { + "ms": { + "path": "beat.stats.info.uptime.ms", + "type": "alias" + } + } + } + } + }, + "memstats": { + "properties": { + "gc_next": { + "path": "beat.stats.memstats.gc_next", + "type": "alias" + }, + "memory_alloc": { + "path": "beat.stats.memstats.memory.alloc", + "type": "alias" + }, + "memory_total": { + "path": "beat.stats.memstats.memory.total", + "type": "alias" + }, + "rss": { + "path": "beat.stats.memstats.rss", + "type": "alias" + } + } + } + } + }, + "libbeat": { + "properties": { + "config": { + "properties": { + "module": { + "properties": { + "running": { + "path": "beat.stats.libbeat.config.running", + "type": "alias" + }, + "starts": { + "path": "beat.stats.libbeat.config.starts", + "type": "alias" + }, + "stops": { + "path": "beat.stats.libbeat.config.stops", + "type": "alias" + } + } + } + } + }, + "output": { + "properties": { + "events": { + "properties": { + "acked": { + "path": "beat.stats.libbeat.output.events.acked", + "type": "alias" + }, + "active": { + "path": "beat.stats.libbeat.output.events.active", + "type": "alias" + }, + "batches": { + "path": "beat.stats.libbeat.output.events.batches", + "type": "alias" + }, + "dropped": { + "path": "beat.stats.libbeat.output.events.dropped", + "type": "alias" + }, + "duplicated": { + "path": "beat.stats.libbeat.output.events.duplicates", + "type": "alias" + }, + "failed": { + "path": "beat.stats.libbeat.output.events.failed", + "type": "alias" + }, + "toomany": { + "path": "beat.stats.libbeat.output.events.toomany", + "type": "alias" + }, + "total": { + "path": "beat.stats.libbeat.output.events.total", + "type": "alias" + } + } + }, + "read": { + "properties": { + "bytes": { + "path": "beat.stats.libbeat.output.read.bytes", + "type": "alias" + }, + "errors": { + "path": "beat.stats.libbeat.output.read.errors", + "type": "alias" + } + } + }, + "type": { + "path": "beat.stats.libbeat.output.type", + "type": "alias" + }, + "write": { + "properties": { + "bytes": { + "path": "beat.stats.libbeat.output.write.bytes", + "type": "alias" + }, + "errors": { + "path": "beat.stats.libbeat.output.write.errors", + "type": "alias" + } + } + } + } + }, + "pipeline": { + "properties": { + "clients": { + "path": "beat.stats.libbeat.pipeline.clients", + "type": "alias" + }, + "event": { + "properties": { + "active": { + "path": "beat.stats.libbeat.pipeline.events.active", + "type": "alias" + }, + "dropped": { + "path": "beat.stats.libbeat.pipeline.events.dropped", + "type": "alias" + }, + "failed": { + "path": "beat.stats.libbeat.pipeline.events.failed", + "type": "alias" + }, + "filtered": { + "path": "beat.stats.libbeat.pipeline.events.filtered", + "type": "alias" + }, + "published": { + "path": "beat.stats.libbeat.pipeline.events.published", + "type": "alias" + }, + "retry": { + "path": "beat.stats.libbeat.pipeline.events.retry", + "type": "alias" + }, + "total": { + "path": "beat.stats.libbeat.pipeline.events.total", + "type": "alias" + } + } + }, + "queue": { + "properties": { + "acked": { + "path": "beat.stats.libbeat.pipeline.queue.acked", + "type": "alias" + } + } + } + } + } + } + }, + "system": { + "properties": { + "cpu": { + "properties": { + "cores": { + "path": "beat.stats.system.cpu.cores", + "type": "alias" + } + } + }, + "load": { + "properties": { + "1": { + "path": "beat.stats.system.load.1", + "type": "alias" + }, + "15": { + "path": "beat.stats.system.load.15", + "type": "alias" + }, + "5": { + "path": "beat.stats.system.load.5", + "type": "alias" + }, + "norm": { + "properties": { + "1": { + "path": "beat.stats.system.load.norm.1", + "type": "alias" + }, + "15": { + "path": "beat.stats.system.load.norm.15", + "type": "alias" + }, + "5": { + "path": "beat.stats.system.load.norm.5", + "type": "alias" + } + } + } + } + } + } + } + } + } + } + }, + "ccr_auto_follow_stats": { + "properties": { + "follower": { + "properties": { + "failed_read_requests": { + "path": "elasticsearch.ccr.requests.failed.read.count", + "type": "alias" + } + } + }, + "number_of_failed_follow_indices": { + "path": "elasticsearch.ccr.auto_follow.failed.follow_indices.count", + "type": "alias" + }, + "number_of_failed_remote_cluster_state_requests": { + "path": "elasticsearch.ccr.auto_follow.failed.remote_cluster_state_requests.count", + "type": "alias" + }, + "number_of_successful_follow_indices": { + "path": "elasticsearch.ccr.auto_follow.success.follow_indices.count", + "type": "alias" + } + } + }, + "ccr_stats": { + "properties": { + "bytes_read": { + "path": "elasticsearch.ccr.bytes_read", + "type": "alias" + }, + "failed_read_requests": { + "path": "elasticsearch.ccr.requests.failed.read.count", + "type": "alias" + }, + "failed_write_requests": { + "path": "elasticsearch.ccr.requests.failed.write.count", + "type": "alias" + }, + "follower_aliases_version": { + "path": "elasticsearch.ccr.follower.aliases_version", + "type": "alias" + }, + "follower_global_checkpoint": { + "path": "elasticsearch.ccr.follower.global_checkpoint", + "type": "alias" + }, + "follower_index": { + "path": "elasticsearch.ccr.follower.index", + "type": "alias" + }, + "follower_mapping_version": { + "path": "elasticsearch.ccr.follower.mapping_version", + "type": "alias" + }, + "follower_max_seq_no": { + "path": "elasticsearch.ccr.follower.max_seq_no", + "type": "alias" + }, + "follower_settings_version": { + "path": "elasticsearch.ccr.follower.settings_version", + "type": "alias" + }, + "last_requested_seq_no": { + "path": "elasticsearch.ccr.last_requested_seq_no", + "type": "alias" + }, + "leader_global_checkpoint": { + "path": "elasticsearch.ccr.leader.global_checkpoint", + "type": "alias" + }, + "leader_index": { + "path": "elasticsearch.ccr.leader.index", + "type": "alias" + }, + "leader_max_seq_no": { + "path": "elasticsearch.ccr.leader.max_seq_no", + "type": "alias" + }, + "operations_read": { + "path": "elasticsearch.ccr.follower.operations.read.count", + "type": "alias" + }, + "operations_written": { + "path": "elasticsearch.ccr.follower.operations_written", + "type": "alias" + }, + "outstanding_read_requests": { + "path": "elasticsearch.ccr.requests.outstanding.read.count", + "type": "alias" + }, + "outstanding_write_requests": { + "path": "elasticsearch.ccr.requests.outstanding.write.count", + "type": "alias" + }, + "remote_cluster": { + "path": "elasticsearch.ccr.remote_cluster", + "type": "alias" + }, + "shard_id": { + "path": "elasticsearch.ccr.follower.shard.number", + "type": "alias" + }, + "successful_read_requests": { + "path": "elasticsearch.ccr.requests.successful.read.count", + "type": "alias" + }, + "successful_write_requests": { + "path": "elasticsearch.ccr.requests.successful.write.count", + "type": "alias" + }, + "total_read_remote_exec_time_millis": { + "path": "elasticsearch.ccr.total_time.read.remote_exec.ms", + "type": "alias" + }, + "total_read_time_millis": { + "path": "elasticsearch.ccr.total_time.read.ms", + "type": "alias" + }, + "total_write_time_millis": { + "path": "elasticsearch.ccr.total_time.write.ms", + "type": "alias" + }, + "write_buffer_operation_count": { + "path": "elasticsearch.ccr.write_buffer.operation.count", + "type": "alias" + }, + "write_buffer_size_in_bytes": { + "path": "elasticsearch.ccr.write_buffer.size.bytes", + "type": "alias" + } + } + }, + "ceph": { + "properties": { + "cluster_disk": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "cluster_health": { + "properties": { + "overall_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "timechecks": { + "properties": { + "epoch": { + "type": "long" + }, + "round": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + } + } + } + }, + "cluster_status": { + "properties": { + "degraded": { + "properties": { + "objects": { + "type": "long" + }, + "ratio": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "total": { + "type": "long" + } + } + }, + "misplace": { + "properties": { + "objects": { + "type": "long" + }, + "ratio": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "total": { + "type": "long" + } + } + }, + "osd": { + "properties": { + "epoch": { + "type": "long" + }, + "full": { + "type": "boolean" + }, + "nearfull": { + "type": "boolean" + }, + "num_in_osds": { + "type": "long" + }, + "num_osds": { + "type": "long" + }, + "num_remapped_pgs": { + "type": "long" + }, + "num_up_osds": { + "type": "long" + } + } + }, + "pg": { + "properties": { + "avail_bytes": { + "type": "long" + }, + "data_bytes": { + "type": "long" + }, + "total_bytes": { + "type": "long" + }, + "used_bytes": { + "type": "long" + } + } + }, + "pg_state": { + "properties": { + "count": { + "type": "long" + }, + "state_name": { + "type": "long" + }, + "version": { + "type": "long" + } + } + }, + "traffic": { + "properties": { + "read_bytes": { + "type": "long" + }, + "read_op_per_sec": { + "type": "long" + }, + "write_bytes": { + "type": "long" + }, + "write_op_per_sec": { + "type": "long" + } + } + }, + "version": { + "type": "long" + } + } + }, + "mgr_osd_perf": { + "properties": { + "id": { + "type": "long" + }, + "stats": { + "properties": { + "apply_latency_ms": { + "type": "long" + }, + "apply_latency_ns": { + "type": "long" + }, + "commit_latency_ms": { + "type": "long" + }, + "commit_latency_ns": { + "type": "long" + } + } + } + } + }, + "mgr_osd_pool_stats": { + "properties": { + "client_io_rate": { + "type": "object" + }, + "pool_id": { + "type": "long" + }, + "pool_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "monitor_health": { + "properties": { + "available": { + "properties": { + "kb": { + "type": "long" + }, + "pct": { + "type": "long" + } + } + }, + "health": { + "ignore_above": 1024, + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "store_stats": { + "properties": { + "last_updated": { + "type": "long" + }, + "log": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "misc": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "sst": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "total": { + "properties": { + "kb": { + "type": "long" + } + } + }, + "used": { + "properties": { + "kb": { + "type": "long" + } + } + } + } + }, + "osd_df": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "device_class": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pg_num": { + "type": "long" + }, + "total": { + "properties": { + "byte": { + "type": "long" + } + } + }, + "used": { + "properties": { + "byte": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "osd_tree": { + "properties": { + "children": { + "ignore_above": 1024, + "type": "keyword" + }, + "crush_weight": { + "type": "float" + }, + "depth": { + "type": "long" + }, + "device_class": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "father": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "primary_affinity": { + "type": "float" + }, + "reweight": { + "type": "long" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "type_id": { + "type": "long" + } + } + }, + "pool_disk": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "stats": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "objects": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "kb": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "origin": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "target": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "cluster_state": { + "properties": { + "master_node": { + "path": "elasticsearch.cluster.stats.state.master_node", + "type": "alias" + }, + "nodes_hash": { + "path": "elasticsearch.cluster.stats.state.nodes_hash", + "type": "alias" + }, + "state_uuid": { + "path": "elasticsearch.cluster.stats.state.state_uuid", + "type": "alias" + }, + "status": { + "path": "elasticsearch.cluster.stats.status", + "type": "alias" + }, + "version": { + "path": "elasticsearch.cluster.stats.state.version", + "type": "alias" + } + } + }, + "cluster_stats": { + "properties": { + "indices": { + "properties": { + "count": { + "path": "elasticsearch.cluster.stats.indices.total", + "type": "alias" + }, + "shards": { + "properties": { + "total": { + "path": "elasticsearch.cluster.stats.indices.shards.count", + "type": "alias" + } + } + } + } + }, + "nodes": { + "properties": { + "count": { + "properties": { + "total": { + "path": "elasticsearch.cluster.stats.nodes.count", + "type": "alias" + } + } + }, + "jvm": { + "properties": { + "max_uptime_in_millis": { + "path": "elasticsearch.cluster.stats.nodes.jvm.max_uptime.ms", + "type": "alias" + }, + "mem": { + "properties": { + "heap_max_in_bytes": { + "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.max.bytes", + "type": "alias" + }, + "heap_used_in_bytes": { + "path": "elasticsearch.cluster.stats.nodes.jvm.memory.heap.used.bytes", + "type": "alias" + } + } + } + } + } + } + } + } + }, + "cluster_uuid": { + "path": "elasticsearch.cluster.id", + "type": "alias" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "consul": { + "properties": { + "agent": { + "properties": { + "autopilot": { + "properties": { + "healthy": { + "type": "boolean" + } + } + }, + "runtime": { + "properties": { + "alloc": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "garbage_collector": { + "properties": { + "pause": { + "properties": { + "current": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "total": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "runs": { + "type": "long" + } + } + }, + "goroutines": { + "type": "long" + }, + "heap_objects": { + "type": "long" + }, + "malloc_count": { + "type": "long" + }, + "sys": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "container": { + "properties": { + "cpu": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "memory": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "couchbase": { + "properties": { + "bucket": { + "properties": { + "data": { + "properties": { + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "disk": { + "properties": { + "fetches": { + "type": "double" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "item_count": { + "type": "long" + }, + "memory": { + "properties": { + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ops_per_sec": { + "type": "double" + }, + "quota": { + "properties": { + "ram": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "use": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cluster": { + "properties": { + "hdd": { + "properties": { + "free": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "quota": { + "properties": { + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "by_data": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "value": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "max_bucket_count": { + "type": "long" + }, + "quota": { + "properties": { + "index_memory": { + "properties": { + "mb": { + "type": "double" + } + } + }, + "memory": { + "properties": { + "mb": { + "type": "double" + } + } + } + } + }, + "ram": { + "properties": { + "quota": { + "properties": { + "total": { + "properties": { + "per_node": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "value": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "used": { + "properties": { + "per_node": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "value": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "by_data": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "value": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "node": { + "properties": { + "cmd_get": { + "type": "double" + }, + "couch": { + "properties": { + "docs": { + "properties": { + "data_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "disk_size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "spatial": { + "properties": { + "data_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "disk_size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "views": { + "properties": { + "data_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "disk_size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "cpu_utilization_rate": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "current_items": { + "properties": { + "total": { + "type": "long" + }, + "value": { + "type": "long" + } + } + }, + "ep_bg_fetched": { + "type": "long" + }, + "get_hits": { + "type": "double" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "mcd_memory": { + "properties": { + "allocated": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "reserved": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "free": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "ops": { + "type": "double" + }, + "swap": { + "properties": { + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "uptime": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "vb_replica_curr_items": { + "type": "long" + } + } + } + } + }, + "couchdb": { + "properties": { + "server": { + "properties": { + "couchdb": { + "properties": { + "auth_cache_hits": { + "type": "long" + }, + "auth_cache_misses": { + "type": "long" + }, + "database_reads": { + "type": "long" + }, + "database_writes": { + "type": "long" + }, + "open_databases": { + "type": "long" + }, + "open_os_files": { + "type": "long" + }, + "request_time": { + "type": "long" + } + } + }, + "httpd": { + "properties": { + "bulk_requests": { + "type": "long" + }, + "clients_requesting_changes": { + "type": "long" + }, + "requests": { + "type": "long" + }, + "temporary_view_reads": { + "type": "long" + }, + "view_reads": { + "type": "long" + } + } + }, + "httpd_request_methods": { + "properties": { + "COPY": { + "type": "long" + }, + "DELETE": { + "type": "long" + }, + "GET": { + "type": "long" + }, + "HEAD": { + "type": "long" + }, + "POST": { + "type": "long" + }, + "PUT": { + "type": "long" + } + } + }, + "httpd_status_codes": { + "properties": { + "200": { + "type": "long" + }, + "201": { + "type": "long" + }, + "202": { + "type": "long" + }, + "301": { + "type": "long" + }, + "304": { + "type": "long" + }, + "400": { + "type": "long" + }, + "401": { + "type": "long" + }, + "403": { + "type": "long" + }, + "404": { + "type": "long" + }, + "405": { + "type": "long" + }, + "409": { + "type": "long" + }, + "412": { + "type": "long" + }, + "500": { + "type": "long" + } + } + } + } + } + } + }, + "data_stream": { + "properties": { + "dataset": { + "type": "constant_keyword" + }, + "namespace": { + "type": "constant_keyword" + }, + "type": { + "type": "constant_keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "command": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ip_addresses": { + "type": "ip" + }, + "labels": { + "type": "object" + }, + "size": { + "properties": { + "root_fs": { + "type": "long" + }, + "rw": { + "type": "long" + } + } + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cpu": { + "properties": { + "core": { + "properties": { + "*": { + "properties": { + "norm": { + "properties": { + "pct": { + "type": "object" + } + } + }, + "pct": { + "type": "object" + }, + "ticks": { + "type": "object" + } + } + } + } + }, + "kernel": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "system": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "user": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + } + } + }, + "diskio": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + }, + "ops": { + "type": "long" + }, + "queued": { + "type": "long" + }, + "rate": { + "type": "long" + }, + "service_time": { + "type": "long" + }, + "wait_time": { + "type": "long" + } + } + }, + "summary": { + "properties": { + "bytes": { + "type": "long" + }, + "ops": { + "type": "long" + }, + "queued": { + "type": "long" + }, + "rate": { + "type": "long" + }, + "service_time": { + "type": "long" + }, + "wait_time": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + }, + "ops": { + "type": "long" + }, + "queued": { + "type": "long" + }, + "rate": { + "type": "long" + }, + "service_time": { + "type": "long" + }, + "wait_time": { + "type": "long" + } + } + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "actor": { + "properties": { + "attributes": { + "type": "object" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "from": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "healthcheck": { + "properties": { + "event": { + "properties": { + "end_date": { + "type": "date" + }, + "exit_code": { + "type": "long" + }, + "output": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_date": { + "type": "date" + } + } + }, + "failingstreak": { + "type": "long" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "image": { + "properties": { + "created": { + "type": "date" + }, + "id": { + "properties": { + "current": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "size": { + "properties": { + "regular": { + "type": "long" + }, + "virtual": { + "type": "long" + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "info": { + "properties": { + "containers": { + "properties": { + "paused": { + "type": "long" + }, + "running": { + "type": "long" + }, + "stopped": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "images": { + "type": "long" + } + } + }, + "memory": { + "properties": { + "commit": { + "properties": { + "peak": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "fail": { + "properties": { + "count": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "limit": { + "type": "long" + }, + "private_working_set": { + "properties": { + "total": { + "type": "long" + } + } + }, + "rss": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "total": { + "type": "long" + } + } + }, + "stats": { + "properties": { + "*": { + "type": "object" + } + } + }, + "usage": { + "properties": { + "max": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "total": { + "type": "long" + } + } + } + } + }, + "network": { + "properties": { + "inbound": { + "properties": { + "bytes": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "interface": { + "ignore_above": 1024, + "type": "keyword" + }, + "outbound": { + "properties": { + "bytes": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "network_summary": { + "properties": { + "icmp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "ip": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "properties": { + "id": { + "type": "long" + }, + "pid": { + "type": "long" + } + } + }, + "tcp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "udp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "udp_lite": { + "properties": { + "*": { + "type": "object" + } + } + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "elasticsearch": { + "properties": { + "ccr": { + "properties": { + "auto_follow": { + "properties": { + "failed": { + "properties": { + "follow_indices": { + "properties": { + "count": { + "type": "long" + } + } + }, + "remote_cluster_state_requests": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "success": { + "properties": { + "follow_indices": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "bytes_read": { + "type": "long" + }, + "follower": { + "properties": { + "aliases_version": { + "type": "long" + }, + "global_checkpoint": { + "type": "long" + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "mapping_version": { + "type": "long" + }, + "max_seq_no": { + "type": "long" + }, + "operations": { + "properties": { + "read": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "operations_written": { + "type": "long" + }, + "settings_version": { + "type": "long" + }, + "shard": { + "properties": { + "number": { + "type": "long" + } + } + }, + "time_since_last_read": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "last_requested_seq_no": { + "type": "long" + }, + "leader": { + "properties": { + "global_checkpoint": { + "type": "long" + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "max_seq_no": { + "type": "long" + } + } + }, + "read_exceptions": { + "type": "nested" + }, + "remote_cluster": { + "ignore_above": 1024, + "type": "keyword" + }, + "requests": { + "properties": { + "failed": { + "properties": { + "read": { + "properties": { + "count": { + "type": "long" + } + } + }, + "write": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "outstanding": { + "properties": { + "read": { + "properties": { + "count": { + "type": "long" + } + } + }, + "write": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "successful": { + "properties": { + "read": { + "properties": { + "count": { + "type": "long" + } + } + }, + "write": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "shard_id": { + "type": "long" + }, + "total_time": { + "properties": { + "read": { + "properties": { + "ms": { + "type": "long" + }, + "remote_exec": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "write": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "write_buffer": { + "properties": { + "operation": { + "properties": { + "count": { + "type": "long" + } + } + }, + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "cluster": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pending_task": { + "properties": { + "insert_order": { + "type": "long" + }, + "priority": { + "type": "long" + }, + "source": { + "ignore_above": 1024, + "type": "keyword" + }, + "time_in_queue": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "state": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "stats": { + "properties": { + "indices": { + "properties": { + "fielddata": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "shards": { + "properties": { + "count": { + "type": "long" + }, + "docs": { + "properties": { + "total": { + "type": "long" + } + } + }, + "primaries": { + "type": "long" + } + } + }, + "store": { + "properties": { + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "total": { + "type": "long" + } + } + }, + "license": { + "properties": { + "expiry_date_in_millis": { + "type": "long" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "nodes": { + "properties": { + "count": { + "type": "long" + }, + "data": { + "type": "long" + }, + "fs": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "jvm": { + "properties": { + "max_uptime": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "memory": { + "properties": { + "heap": { + "properties": { + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "master": { + "type": "long" + }, + "stats": { + "properties": { + "data": { + "type": "long" + } + } + } + } + }, + "stack": { + "properties": { + "apm": { + "properties": { + "found": { + "type": "boolean" + } + } + }, + "xpack": { + "properties": { + "ccr": { + "properties": { + "available": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + } + } + } + } + } + } + }, + "state": { + "properties": { + "master_node": { + "ignore_above": 1024, + "type": "keyword" + }, + "nodes_hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "enrich": { + "properties": { + "executed_searches": { + "properties": { + "total": { + "type": "long" + } + } + }, + "executing_policy": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "task": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "cancellable": { + "type": "boolean" + }, + "id": { + "type": "long" + }, + "parent_task_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "task": { + "ignore_above": 1024, + "type": "keyword" + }, + "time": { + "properties": { + "running": { + "properties": { + "nano": { + "type": "long" + } + } + }, + "start": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "queue": { + "properties": { + "size": { + "type": "long" + } + } + }, + "remote_requests": { + "properties": { + "current": { + "type": "long" + }, + "total": { + "type": "long" + } + } + } + } + }, + "index": { + "properties": { + "created": { + "type": "long" + }, + "hidden": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "primaries": { + "properties": { + "docs": { + "properties": { + "count": { + "type": "long" + }, + "deleted": { + "type": "long" + } + } + }, + "indexing": { + "properties": { + "index_time_in_millis": { + "type": "long" + }, + "index_total": { + "type": "long" + }, + "throttle_time_in_millis": { + "type": "long" + } + } + }, + "merges": { + "properties": { + "total_size_in_bytes": { + "type": "long" + } + } + }, + "query_cache": { + "properties": { + "hit_count": { + "type": "long" + }, + "memory_size_in_bytes": { + "type": "long" + }, + "miss_count": { + "type": "long" + } + } + }, + "refresh": { + "properties": { + "external_total_time_in_millis": { + "type": "long" + }, + "total_time_in_millis": { + "type": "long" + } + } + }, + "request_cache": { + "properties": { + "evictions": { + "type": "long" + }, + "hit_count": { + "type": "long" + }, + "memory_size_in_bytes": { + "type": "long" + }, + "miss_count": { + "type": "long" + } + } + }, + "search": { + "properties": { + "query_time_in_millis": { + "type": "long" + }, + "query_total": { + "type": "long" + } + } + }, + "segments": { + "properties": { + "count": { + "type": "long" + }, + "doc_values_memory_in_bytes": { + "type": "long" + }, + "fixed_bit_set_memory_in_bytes": { + "type": "long" + }, + "index_writer_memory_in_bytes": { + "type": "long" + }, + "memory_in_bytes": { + "type": "long" + }, + "norms_memory_in_bytes": { + "type": "long" + }, + "points_memory_in_bytes": { + "type": "long" + }, + "stored_fields_memory_in_bytes": { + "type": "long" + }, + "term_vectors_memory_in_bytes": { + "type": "long" + }, + "terms_memory_in_bytes": { + "type": "long" + }, + "version_map_memory_in_bytes": { + "type": "long" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "type": "long" + } + } + } + } + }, + "recovery": { + "properties": { + "id": { + "type": "long" + }, + "index": { + "properties": { + "files": { + "properties": { + "percent": { + "ignore_above": 1024, + "type": "keyword" + }, + "recovered": { + "type": "long" + }, + "reused": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "size": { + "properties": { + "recovered_in_bytes": { + "type": "long" + }, + "reused_in_bytes": { + "type": "long" + }, + "total_in_bytes": { + "type": "long" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "primary": { + "type": "boolean" + }, + "source": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport_address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "stage": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "stop_time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "target": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport_address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "total_time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "translog": { + "properties": { + "percent": { + "ignore_above": 1024, + "type": "keyword" + }, + "total": { + "type": "long" + }, + "total_on_start": { + "type": "long" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "verify_index": { + "properties": { + "check_index_time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "total_time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "shards": { + "properties": { + "primaries": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "primaries": { + "properties": { + "bulk": { + "properties": { + "operations": { + "properties": { + "count": { + "type": "long" + } + } + }, + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "time": { + "properties": { + "avg": { + "properties": { + "bytes": { + "type": "long" + }, + "ms": { + "type": "long" + } + } + }, + "count": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "docs": { + "properties": { + "count": { + "type": "long" + }, + "deleted": { + "type": "long" + } + } + }, + "indexing": { + "properties": { + "index": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "search": { + "properties": { + "query": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "segments": { + "properties": { + "count": { + "type": "long" + }, + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "store": { + "properties": { + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "total": { + "properties": { + "bulk": { + "properties": { + "operations": { + "properties": { + "count": { + "type": "long" + } + } + }, + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "time": { + "properties": { + "avg": { + "properties": { + "bytes": { + "type": "long" + }, + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "docs": { + "properties": { + "count": { + "type": "long" + }, + "deleted": { + "type": "long" + } + } + }, + "indexing": { + "properties": { + "index": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "is_throttled": { + "type": "boolean" + }, + "throttle_time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "search": { + "properties": { + "query": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "segments": { + "properties": { + "count": { + "type": "long" + }, + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "store": { + "properties": { + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "total": { + "properties": { + "bulk": { + "properties": { + "avg_size_in_bytes": { + "type": "long" + }, + "avg_time_in_millis": { + "type": "long" + }, + "total_operations": { + "type": "long" + }, + "total_size_in_bytes": { + "type": "long" + }, + "total_time_in_millis": { + "type": "long" + } + } + }, + "docs": { + "properties": { + "count": { + "type": "long" + }, + "deleted": { + "type": "long" + } + } + }, + "fielddata": { + "properties": { + "evictions": { + "type": "long" + }, + "memory_size_in_bytes": { + "type": "long" + } + } + }, + "indexing": { + "properties": { + "index_time_in_millis": { + "type": "long" + }, + "index_total": { + "type": "long" + }, + "throttle_time_in_millis": { + "type": "long" + } + } + }, + "merges": { + "properties": { + "total_size_in_bytes": { + "type": "long" + } + } + }, + "query_cache": { + "properties": { + "evictions": { + "type": "long" + }, + "hit_count": { + "type": "long" + }, + "memory_size_in_bytes": { + "type": "long" + }, + "miss_count": { + "type": "long" + } + } + }, + "refresh": { + "properties": { + "external_total_time_in_millis": { + "type": "long" + }, + "total_time_in_millis": { + "type": "long" + } + } + }, + "request_cache": { + "properties": { + "evictions": { + "type": "long" + }, + "hit_count": { + "type": "long" + }, + "memory_size_in_bytes": { + "type": "long" + }, + "miss_count": { + "type": "long" + } + } + }, + "search": { + "properties": { + "query_time_in_millis": { + "type": "long" + }, + "query_total": { + "type": "long" + } + } + }, + "segments": { + "properties": { + "count": { + "type": "long" + }, + "doc_values_memory_in_bytes": { + "type": "long" + }, + "fixed_bit_set_memory_in_bytes": { + "type": "long" + }, + "index_writer_memory_in_bytes": { + "type": "long" + }, + "memory_in_bytes": { + "type": "long" + }, + "norms_memory_in_bytes": { + "type": "long" + }, + "points_memory_in_bytes": { + "type": "long" + }, + "stored_fields_memory_in_bytes": { + "type": "long" + }, + "term_vectors_memory_in_bytes": { + "type": "long" + }, + "terms_memory_in_bytes": { + "type": "long" + }, + "version_map_memory_in_bytes": { + "type": "long" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "type": "long" + } + } + } + } + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ml": { + "properties": { + "job": { + "properties": { + "data": { + "properties": { + "invalid_date": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "data_counts": { + "properties": { + "invalid_date_count": { + "type": "long" + }, + "processed_record_count": { + "type": "long" + } + } + }, + "forecasts_stats": { + "properties": { + "total": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "model_size": { + "properties": { + "memory_status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "node": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "jvm": { + "properties": { + "memory": { + "properties": { + "heap": { + "properties": { + "init": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "nonheap": { + "properties": { + "init": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "master": { + "type": "boolean" + }, + "mlockall": { + "type": "boolean" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "process": { + "properties": { + "mlockall": { + "type": "boolean" + } + } + }, + "stats": { + "properties": { + "fs": { + "properties": { + "io_stats": { + "properties": { + "total": { + "properties": { + "operations": { + "properties": { + "count": { + "type": "long" + } + } + }, + "read": { + "properties": { + "operations": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "write": { + "properties": { + "operations": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "summary": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "free": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "total": { + "properties": { + "available_in_bytes": { + "type": "long" + }, + "total_in_bytes": { + "type": "long" + } + } + } + } + }, + "indexing_pressure": { + "properties": { + "memory": { + "properties": { + "current": { + "properties": { + "all": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "combined_coordinating_and_primary": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "coordinating": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "primary": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "replica": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "limit_in_bytes": { + "type": "long" + }, + "total": { + "properties": { + "all": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "combined_coordinating_and_primary": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "coordinating": { + "properties": { + "bytes": { + "type": "long" + }, + "rejections": { + "type": "long" + } + } + }, + "primary": { + "properties": { + "bytes": { + "type": "long" + }, + "rejections": { + "type": "long" + } + } + }, + "replica": { + "properties": { + "bytes": { + "type": "long" + }, + "rejections": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "indices": { + "properties": { + "bulk": { + "properties": { + "avg_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "avg_time": { + "properties": { + "bytes": { + "type": "long" + }, + "ms": { + "type": "long" + } + } + }, + "operations": { + "properties": { + "total": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "total_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total_time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "docs": { + "properties": { + "count": { + "type": "long" + }, + "deleted": { + "type": "long" + } + } + }, + "fielddata": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "indexing": { + "properties": { + "index_time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "index_total": { + "properties": { + "count": { + "type": "long" + } + } + }, + "throttle_time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "query_cache": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "request_cache": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "search": { + "properties": { + "query_time": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "query_total": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "segments": { + "properties": { + "count": { + "type": "long" + }, + "doc_values": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "fixed_bit_set": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "index_writer": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "norms": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "points": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "stored_fields": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "term_vectors": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "terms": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "version_map": { + "properties": { + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "store": { + "properties": { + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "ingest": { + "properties": { + "total": { + "properties": { + "count": { + "type": "long" + }, + "current": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "time_in_millis": { + "type": "long" + } + } + } + } + }, + "jvm": { + "properties": { + "gc": { + "properties": { + "collectors": { + "properties": { + "old": { + "properties": { + "collection": { + "properties": { + "count": { + "type": "long" + }, + "ms": { + "type": "long" + } + } + } + } + }, + "young": { + "properties": { + "collection": { + "properties": { + "count": { + "type": "long" + }, + "ms": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "mem": { + "properties": { + "heap": { + "properties": { + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "double" + } + } + } + } + }, + "pools": { + "properties": { + "old": { + "properties": { + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "peak": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "peak_max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "survivor": { + "properties": { + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "peak": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "peak_max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "young": { + "properties": { + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "peak": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "peak_max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + } + } + } + } + }, + "os": { + "properties": { + "cgroup": { + "properties": { + "cpu": { + "properties": { + "cfs": { + "properties": { + "quota": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "stat": { + "properties": { + "elapsed_periods": { + "properties": { + "count": { + "type": "long" + } + } + }, + "time_throttled": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "times_throttled": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "cpuacct": { + "properties": { + "usage": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "control_group": { + "ignore_above": 1024, + "type": "keyword" + }, + "limit": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "usage": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "cpu": { + "properties": { + "load_avg": { + "properties": { + "1m": { + "type": "half_float" + } + } + } + } + } + } + }, + "process": { + "properties": { + "cpu": { + "properties": { + "pct": { + "type": "double" + } + } + } + } + }, + "thread_pool": { + "properties": { + "bulk": { + "properties": { + "queue": { + "properties": { + "count": { + "type": "long" + } + } + }, + "rejected": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "get": { + "properties": { + "queue": { + "properties": { + "count": { + "type": "long" + } + } + }, + "rejected": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "index": { + "properties": { + "queue": { + "properties": { + "count": { + "type": "long" + } + } + }, + "rejected": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "search": { + "properties": { + "queue": { + "properties": { + "count": { + "type": "long" + } + } + }, + "rejected": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "write": { + "properties": { + "queue": { + "properties": { + "count": { + "type": "long" + } + } + }, + "rejected": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shard": { + "properties": { + "number": { + "type": "long" + }, + "primary": { + "type": "boolean" + }, + "relocating_node": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source_node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "envoyproxy": { + "properties": { + "server": { + "properties": { + "cluster_manager": { + "properties": { + "active_clusters": { + "type": "long" + }, + "cluster_added": { + "type": "long" + }, + "cluster_modified": { + "type": "long" + }, + "cluster_removed": { + "type": "long" + }, + "cluster_updated": { + "type": "long" + }, + "cluster_updated_via_merge": { + "type": "long" + }, + "update_merge_cancelled": { + "type": "long" + }, + "update_out_of_merge_window": { + "type": "long" + }, + "warming_clusters": { + "type": "long" + } + } + }, + "filesystem": { + "properties": { + "flushed_by_timer": { + "type": "long" + }, + "reopen_failed": { + "type": "long" + }, + "write_buffered": { + "type": "long" + }, + "write_completed": { + "type": "long" + }, + "write_failed": { + "type": "long" + }, + "write_total_buffered": { + "type": "long" + } + } + }, + "http2": { + "properties": { + "header_overflow": { + "type": "long" + }, + "headers_cb_no_stream": { + "type": "long" + }, + "rx_messaging_error": { + "type": "long" + }, + "rx_reset": { + "type": "long" + }, + "too_many_header_frames": { + "type": "long" + }, + "trailers": { + "type": "long" + }, + "tx_reset": { + "type": "long" + } + } + }, + "listener_manager": { + "properties": { + "listener_added": { + "type": "long" + }, + "listener_create_failure": { + "type": "long" + }, + "listener_create_success": { + "type": "long" + }, + "listener_modified": { + "type": "long" + }, + "listener_removed": { + "type": "long" + }, + "listener_stopped": { + "type": "long" + }, + "total_listeners_active": { + "type": "long" + }, + "total_listeners_draining": { + "type": "long" + }, + "total_listeners_warming": { + "type": "long" + } + } + }, + "runtime": { + "properties": { + "admin_overrides_active": { + "type": "long" + }, + "deprecated_feature_use": { + "type": "long" + }, + "load_error": { + "type": "long" + }, + "load_success": { + "type": "long" + }, + "num_keys": { + "type": "long" + }, + "num_layers": { + "type": "long" + }, + "override_dir_exists": { + "type": "long" + }, + "override_dir_not_exists": { + "type": "long" + } + } + }, + "server": { + "properties": { + "concurrency": { + "type": "long" + }, + "days_until_first_cert_expiring": { + "type": "long" + }, + "debug_assertion_failures": { + "type": "long" + }, + "dynamic_unknown_fields": { + "type": "long" + }, + "hot_restart_epoch": { + "type": "long" + }, + "live": { + "type": "long" + }, + "memory_allocated": { + "type": "long" + }, + "memory_heap_size": { + "type": "long" + }, + "parent_connections": { + "type": "long" + }, + "state": { + "type": "long" + }, + "static_unknown_fields": { + "type": "long" + }, + "stats_recent_lookups": { + "type": "long" + }, + "total_connections": { + "type": "long" + }, + "uptime": { + "type": "long" + }, + "version": { + "type": "long" + }, + "watchdog_mega_miss": { + "type": "long" + }, + "watchdog_miss": { + "type": "long" + } + } + }, + "stats": { + "properties": { + "overflow": { + "type": "long" + } + } + } + } + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "etcd": { + "properties": { + "api_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "disk": { + "properties": { + "backend_commit_duration": { + "properties": { + "ns": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + }, + "mvcc_db_total_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "wal_fsync_duration": { + "properties": { + "ns": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + }, + "leader": { + "properties": { + "follower": { + "properties": { + "failed_operations": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "latency": { + "properties": { + "ms": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "leader": { + "ignore_above": 1024, + "type": "keyword" + }, + "success_operations": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "go_memstats_alloc": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "network": { + "properties": { + "client_grpc_received": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "client_grpc_sent": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "self": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "leaderinfo": { + "properties": { + "leader": { + "ignore_above": 1024, + "type": "keyword" + }, + "starttime": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "recv": { + "properties": { + "appendrequest": { + "properties": { + "count": { + "type": "long" + } + } + }, + "bandwidthrate": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "pkgrate": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "send": { + "properties": { + "appendrequest": { + "properties": { + "count": { + "type": "long" + } + } + }, + "bandwidthrate": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "pkgrate": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "starttime": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "grpc_handled": { + "properties": { + "count": { + "type": "long" + } + } + }, + "grpc_started": { + "properties": { + "count": { + "type": "long" + } + } + }, + "has_leader": { + "type": "byte" + }, + "leader_changes": { + "properties": { + "count": { + "type": "long" + } + } + }, + "proposals_committed": { + "properties": { + "count": { + "type": "long" + } + } + }, + "proposals_failed": { + "properties": { + "count": { + "type": "long" + } + } + }, + "proposals_pending": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "store": { + "properties": { + "compareanddelete": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "compareandswap": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "create": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "delete": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "expire": { + "properties": { + "count": { + "type": "long" + } + } + }, + "gets": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "sets": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "update": { + "properties": { + "fail": { + "type": "long" + }, + "success": { + "type": "long" + } + } + }, + "watchers": { + "type": "long" + } + } + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "agent_id_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "faas": { + "properties": { + "coldstart": { + "type": "boolean" + }, + "execution": { + "ignore_above": 1024, + "type": "keyword" + }, + "trigger": { + "properties": { + "request_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + } + } + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "golang": { + "properties": { + "expvar": { + "properties": { + "cmdline": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "heap": { + "properties": { + "allocations": { + "properties": { + "active": { + "type": "long" + }, + "allocated": { + "type": "long" + }, + "frees": { + "type": "long" + }, + "idle": { + "type": "long" + }, + "mallocs": { + "type": "long" + }, + "objects": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "cmdline": { + "ignore_above": 1024, + "type": "keyword" + }, + "gc": { + "properties": { + "cpu_fraction": { + "type": "float" + }, + "next_gc_limit": { + "type": "long" + }, + "pause": { + "properties": { + "avg": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "count": { + "type": "long" + }, + "max": { + "properties": { + "ns": { + "type": "long" + } + } + }, + "sum": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "total_count": { + "type": "long" + }, + "total_pause": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "system": { + "properties": { + "obtained": { + "type": "long" + }, + "released": { + "type": "long" + }, + "stack": { + "type": "long" + }, + "total": { + "type": "long" + } + } + } + } + } + } + }, + "graphite": { + "properties": { + "server": { + "properties": { + "example": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "haproxy": { + "properties": { + "info": { + "properties": { + "busy_polling": { + "type": "long" + }, + "bytes": { + "properties": { + "out": { + "properties": { + "rate": { + "type": "long" + }, + "total": { + "type": "long" + } + } + } + } + }, + "compress": { + "properties": { + "bps": { + "properties": { + "in": { + "type": "long" + }, + "out": { + "type": "long" + }, + "rate_limit": { + "type": "long" + } + } + } + } + }, + "connection": { + "properties": { + "current": { + "type": "long" + }, + "hard_max": { + "type": "long" + }, + "max": { + "type": "long" + }, + "rate": { + "properties": { + "limit": { + "type": "long" + }, + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + }, + "ssl": { + "properties": { + "current": { + "type": "long" + }, + "max": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "total": { + "type": "long" + } + } + }, + "dropped_logs": { + "type": "long" + }, + "failed_resolutions": { + "type": "long" + }, + "idle": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "jobs": { + "type": "long" + }, + "listeners": { + "type": "long" + }, + "memory": { + "properties": { + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "peers": { + "properties": { + "active": { + "type": "long" + }, + "connected": { + "type": "long" + } + } + }, + "pipes": { + "properties": { + "free": { + "type": "long" + }, + "max": { + "type": "long" + }, + "used": { + "type": "long" + } + } + }, + "pool": { + "properties": { + "allocated": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "used": { + "type": "long" + } + } + }, + "process_num": { + "type": "long" + }, + "processes": { + "type": "long" + }, + "requests": { + "properties": { + "max": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "run_queue": { + "type": "long" + }, + "session": { + "properties": { + "rate": { + "properties": { + "limit": { + "type": "long" + }, + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + } + } + }, + "sockets": { + "properties": { + "max": { + "type": "long" + } + } + }, + "ssl": { + "properties": { + "backend": { + "properties": { + "key_rate": { + "properties": { + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + } + } + }, + "cache_misses": { + "type": "long" + }, + "cached_lookups": { + "type": "long" + }, + "frontend": { + "properties": { + "key_rate": { + "properties": { + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + }, + "session_reuse": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "rate": { + "properties": { + "limit": { + "type": "long" + }, + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + } + } + }, + "stopping": { + "type": "long" + }, + "tasks": { + "type": "long" + }, + "threads": { + "type": "long" + }, + "ulimit_n": { + "type": "long" + }, + "unstoppable_jobs": { + "type": "long" + }, + "uptime": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "zlib_mem_usage": { + "properties": { + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + } + } + }, + "stat": { + "properties": { + "agent": { + "properties": { + "check": { + "properties": { + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "fall": { + "type": "long" + }, + "health": { + "type": "long" + }, + "rise": { + "type": "long" + } + } + }, + "code": { + "type": "long" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "fall": { + "type": "long" + }, + "health": { + "type": "long" + }, + "rise": { + "type": "long" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "check": { + "properties": { + "agent": { + "properties": { + "last": { + "type": "long" + } + } + }, + "code": { + "type": "long" + }, + "down": { + "type": "long" + }, + "duration": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "health": { + "properties": { + "fail": { + "type": "long" + }, + "last": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "client": { + "properties": { + "aborted": { + "type": "long" + } + } + }, + "component_type": { + "type": "long" + }, + "compressor": { + "properties": { + "bypassed": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "in": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "out": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "response": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "connection": { + "properties": { + "attempt": { + "properties": { + "total": { + "type": "long" + } + } + }, + "cache": { + "properties": { + "hits": { + "type": "long" + }, + "lookup": { + "properties": { + "total": { + "type": "long" + } + } + } + } + }, + "idle": { + "properties": { + "limit": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "rate": { + "type": "long" + }, + "rate_max": { + "type": "long" + }, + "retried": { + "type": "long" + }, + "reuse": { + "properties": { + "total": { + "type": "long" + } + } + }, + "time": { + "properties": { + "avg": { + "type": "long" + } + } + }, + "total": { + "type": "long" + } + } + }, + "cookie": { + "ignore_above": 1024, + "type": "keyword" + }, + "downtime": { + "type": "long" + }, + "header": { + "properties": { + "rewrite": { + "properties": { + "failed": { + "properties": { + "total": { + "type": "long" + } + } + } + } + } + } + }, + "in": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "last_change": { + "type": "long" + }, + "load_balancing_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "out": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "proxy": { + "properties": { + "id": { + "type": "long" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "queue": { + "properties": { + "limit": { + "type": "long" + }, + "time": { + "properties": { + "avg": { + "type": "long" + } + } + } + } + }, + "request": { + "properties": { + "connection": { + "properties": { + "errors": { + "type": "long" + } + } + }, + "denied": { + "type": "long" + }, + "denied_by_connection_rules": { + "type": "long" + }, + "denied_by_session_rules": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "intercepted": { + "type": "long" + }, + "queued": { + "properties": { + "current": { + "type": "long" + }, + "max": { + "type": "long" + } + } + }, + "rate": { + "properties": { + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + }, + "redispatched": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "response": { + "properties": { + "denied": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "http": { + "properties": { + "1xx": { + "type": "long" + }, + "2xx": { + "type": "long" + }, + "3xx": { + "type": "long" + }, + "4xx": { + "type": "long" + }, + "5xx": { + "type": "long" + }, + "other": { + "type": "long" + } + } + }, + "time": { + "properties": { + "avg": { + "type": "long" + } + } + } + } + }, + "selected": { + "properties": { + "total": { + "type": "long" + } + } + }, + "server": { + "properties": { + "aborted": { + "type": "long" + }, + "active": { + "type": "long" + }, + "backup": { + "type": "long" + }, + "id": { + "type": "long" + } + } + }, + "service_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "session": { + "properties": { + "current": { + "type": "long" + }, + "limit": { + "type": "long" + }, + "max": { + "type": "long" + }, + "rate": { + "properties": { + "limit": { + "type": "long" + }, + "max": { + "type": "long" + }, + "value": { + "type": "long" + } + } + }, + "total": { + "type": "long" + } + } + }, + "source": { + "properties": { + "address": { + "norms": false, + "type": "text" + } + } + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "throttle": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "tracked": { + "properties": { + "id": { + "type": "long" + } + } + }, + "weight": { + "type": "long" + } + } + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "cpu": { + "properties": { + "usage": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "disk": { + "properties": { + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "egress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "ingress": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "headers": { + "type": "object" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "headers": { + "type": "object" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "phrase": { + "ignore_above": 1024, + "type": "keyword" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "index_recovery": { + "properties": { + "shards": { + "properties": { + "start_time_in_millis": { + "path": "elasticsearch.index.recovery.start_time.ms", + "type": "alias" + }, + "stop_time_in_millis": { + "path": "elasticsearch.index.recovery.stop_time.ms", + "type": "alias" + } + } + } + } + }, + "index_stats": { + "properties": { + "index": { + "path": "elasticsearch.index.name", + "type": "alias" + }, + "primaries": { + "properties": { + "docs": { + "properties": { + "count": { + "path": "elasticsearch.index.primaries.docs.count", + "type": "alias" + } + } + }, + "indexing": { + "properties": { + "index_time_in_millis": { + "path": "elasticsearch.index.primaries.indexing.index_time_in_millis", + "type": "alias" + }, + "index_total": { + "path": "elasticsearch.index.primaries.indexing.index_total", + "type": "alias" + }, + "throttle_time_in_millis": { + "path": "elasticsearch.index.primaries.indexing.throttle_time_in_millis", + "type": "alias" + } + } + }, + "merges": { + "properties": { + "total_size_in_bytes": { + "path": "elasticsearch.index.primaries.merges.total_size_in_bytes", + "type": "alias" + } + } + }, + "refresh": { + "properties": { + "total_time_in_millis": { + "path": "elasticsearch.index.primaries.refresh.total_time_in_millis", + "type": "alias" + } + } + }, + "segments": { + "properties": { + "count": { + "path": "elasticsearch.index.primaries.segments.count", + "type": "alias" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "path": "elasticsearch.index.primaries.store.size_in_bytes", + "type": "alias" + } + } + } + } + }, + "total": { + "properties": { + "fielddata": { + "properties": { + "memory_size_in_bytes": { + "path": "elasticsearch.index.total.fielddata.memory_size_in_bytes", + "type": "alias" + } + } + }, + "indexing": { + "properties": { + "index_time_in_millis": { + "path": "elasticsearch.index.total.indexing.index_time_in_millis", + "type": "alias" + }, + "index_total": { + "path": "elasticsearch.index.total.indexing.index_total", + "type": "alias" + }, + "throttle_time_in_millis": { + "path": "elasticsearch.index.total.indexing.throttle_time_in_millis", + "type": "alias" + } + } + }, + "merges": { + "properties": { + "total_size_in_bytes": { + "path": "elasticsearch.index.total.merges.total_size_in_bytes", + "type": "alias" + } + } + }, + "query_cache": { + "properties": { + "memory_size_in_bytes": { + "path": "elasticsearch.index.total.query_cache.memory_size_in_bytes", + "type": "alias" + } + } + }, + "refresh": { + "properties": { + "total_time_in_millis": { + "path": "elasticsearch.index.total.refresh.total_time_in_millis", + "type": "alias" + } + } + }, + "request_cache": { + "properties": { + "memory_size_in_bytes": { + "path": "elasticsearch.index.total.request_cache.memory_size_in_bytes", + "type": "alias" + } + } + }, + "search": { + "properties": { + "query_time_in_millis": { + "path": "elasticsearch.index.total.search.query_time_in_millis", + "type": "alias" + }, + "query_total": { + "path": "elasticsearch.index.total.search.query_total", + "type": "alias" + } + } + }, + "segments": { + "properties": { + "count": { + "path": "elasticsearch.index.total.segments.count", + "type": "alias" + }, + "doc_values_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.doc_values_memory_in_bytes", + "type": "alias" + }, + "fixed_bit_set_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.fixed_bit_set_memory_in_bytes", + "type": "alias" + }, + "index_writer_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.index_writer_memory_in_bytes", + "type": "alias" + }, + "memory_in_bytes": { + "path": "elasticsearch.index.total.segments.memory_in_bytes", + "type": "alias" + }, + "norms_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.norms_memory_in_bytes", + "type": "alias" + }, + "points_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.points_memory_in_bytes", + "type": "alias" + }, + "stored_fields_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.stored_fields_memory_in_bytes", + "type": "alias" + }, + "term_vectors_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.term_vectors_memory_in_bytes", + "type": "alias" + }, + "terms_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.terms_memory_in_bytes", + "type": "alias" + }, + "version_map_memory_in_bytes": { + "path": "elasticsearch.index.total.segments.version_map_memory_in_bytes", + "type": "alias" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "path": "elasticsearch.index.total.store.size_in_bytes", + "type": "alias" + } + } + } + } + } + } + }, + "indices_stats": { + "properties": { + "_all": { + "properties": { + "primaries": { + "properties": { + "indexing": { + "properties": { + "index_time_in_millis": { + "path": "elasticsearch.index.summary.primaries.indexing.index.time.ms", + "type": "alias" + }, + "index_total": { + "path": "elasticsearch.index.summary.primaries.indexing.index.count", + "type": "alias" + } + } + } + } + }, + "total": { + "properties": { + "indexing": { + "properties": { + "index_total": { + "path": "elasticsearch.index.summary.total.indexing.index.count", + "type": "alias" + } + } + }, + "search": { + "properties": { + "query_time_in_millis": { + "path": "elasticsearch.index.summary.total.search.query.time.ms", + "type": "alias" + }, + "query_total": { + "path": "elasticsearch.index.summary.total.search.query.count", + "type": "alias" + } + } + } + } + } + } + } + } + }, + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "job_stats": { + "properties": { + "forecasts_stats": { + "properties": { + "total": { + "path": "elasticsearch.ml.job.forecasts_stats.total", + "type": "alias" + } + } + }, + "job_id": { + "path": "elasticsearch.ml.job.id", + "type": "alias" + } + } + }, + "jolokia": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secured": { + "type": "boolean" + }, + "server": { + "properties": { + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kafka": { + "properties": { + "broker": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "log": { + "properties": { + "flush_rate": { + "type": "float" + } + } + }, + "mbean": { + "ignore_above": 1024, + "type": "keyword" + }, + "messages_in": { + "type": "float" + }, + "net": { + "properties": { + "in": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + }, + "out": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + }, + "rejected": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + } + } + }, + "replication": { + "properties": { + "leader_elections": { + "type": "float" + }, + "unclean_leader_elections": { + "type": "float" + } + } + }, + "request": { + "properties": { + "channel": { + "properties": { + "queue": { + "properties": { + "size": { + "type": "long" + } + } + } + } + }, + "fetch": { + "properties": { + "failed": { + "type": "float" + }, + "failed_per_second": { + "type": "float" + } + } + }, + "produce": { + "properties": { + "failed": { + "type": "float" + }, + "failed_per_second": { + "type": "float" + } + } + } + } + }, + "session": { + "properties": { + "zookeeper": { + "properties": { + "disconnect": { + "type": "float" + }, + "expire": { + "type": "float" + }, + "readonly": { + "type": "float" + }, + "sync": { + "type": "float" + } + } + } + } + }, + "topic": { + "properties": { + "messages_in": { + "type": "float" + }, + "net": { + "properties": { + "in": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + }, + "out": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + }, + "rejected": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + } + } + } + } + } + } + }, + "consumer": { + "properties": { + "bytes_consumed": { + "type": "float" + }, + "fetch_rate": { + "type": "float" + }, + "in": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + }, + "kafka_commits": { + "type": "float" + }, + "max_lag": { + "type": "float" + }, + "mbean": { + "ignore_above": 1024, + "type": "keyword" + }, + "messages_in": { + "type": "float" + }, + "records_consumed": { + "type": "float" + }, + "zookeeper_commits": { + "type": "float" + } + } + }, + "consumergroup": { + "properties": { + "client": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "member_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "consumer_lag": { + "type": "long" + }, + "error": { + "properties": { + "code": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "meta": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "type": "long" + } + } + }, + "partition": { + "properties": { + "id": { + "type": "long" + }, + "offset": { + "properties": { + "newest": { + "type": "long" + }, + "oldest": { + "type": "long" + } + } + }, + "partition": { + "properties": { + "error": { + "properties": { + "code": { + "type": "long" + } + } + }, + "insync_replica": { + "type": "boolean" + }, + "is_leader": { + "type": "boolean" + }, + "leader": { + "type": "long" + }, + "replica": { + "type": "long" + } + } + }, + "topic_broker_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "topic_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "producer": { + "properties": { + "available_buffer_bytes": { + "type": "float" + }, + "batch_size_avg": { + "type": "float" + }, + "batch_size_max": { + "type": "long" + }, + "io_wait": { + "type": "float" + }, + "mbean": { + "ignore_above": 1024, + "type": "keyword" + }, + "message_rate": { + "type": "float" + }, + "out": { + "properties": { + "bytes_per_sec": { + "type": "float" + } + } + }, + "record_error_rate": { + "type": "float" + }, + "record_retry_rate": { + "type": "float" + }, + "record_send_rate": { + "type": "float" + }, + "record_size_avg": { + "type": "float" + }, + "record_size_max": { + "type": "long" + }, + "records_per_request": { + "type": "float" + }, + "request_rate": { + "type": "float" + }, + "response_rate": { + "type": "float" + } + } + }, + "topic": { + "properties": { + "error": { + "properties": { + "code": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "kibana": { + "properties": { + "cluster_actions": { + "properties": { + "kibana": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "overdue": { + "properties": { + "count": { + "type": "long" + }, + "delay": { + "properties": { + "p50": { + "type": "float" + }, + "p99": { + "type": "float" + } + } + } + } + } + } + }, + "cluster_rules": { + "properties": { + "kibana": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "overdue": { + "properties": { + "count": { + "type": "long" + }, + "delay": { + "properties": { + "p50": { + "type": "float" + }, + "p99": { + "type": "float" + } + } + } + } + } + } + }, + "elasticsearch": { + "properties": { + "cluster": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "node_actions": { + "properties": { + "executions": { + "type": "long" + }, + "failures": { + "type": "long" + }, + "kibana": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timeouts": { + "type": "long" + } + } + }, + "node_rules": { + "properties": { + "executions": { + "type": "long" + }, + "failures": { + "type": "long" + }, + "kibana": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timeouts": { + "type": "long" + } + } + }, + "settings": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "locale": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "snapshot": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport_address": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "stats": { + "properties": { + "concurrent_connections": { + "type": "long" + }, + "host": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "kibana": { + "properties": { + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "distro": { + "ignore_above": 1024, + "type": "keyword" + }, + "distroRelease": { + "ignore_above": 1024, + "type": "keyword" + }, + "load": { + "properties": { + "15m": { + "type": "half_float" + }, + "1m": { + "type": "half_float" + }, + "5m": { + "type": "half_float" + } + } + }, + "memory": { + "properties": { + "free_in_bytes": { + "type": "long" + }, + "total_in_bytes": { + "type": "long" + }, + "used_in_bytes": { + "type": "long" + } + } + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "platformRelease": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "event_loop_delay": { + "properties": { + "ms": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "memory": { + "properties": { + "heap": { + "properties": { + "size_limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "uptime": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "resident_set_size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "uptime": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "request": { + "properties": { + "disconnects": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "response_time": { + "properties": { + "avg": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "max": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "snapshot": { + "type": "boolean" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "usage": { + "properties": { + "index": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "status": { + "properties": { + "metrics": { + "properties": { + "concurrent_connections": { + "type": "long" + }, + "requests": { + "properties": { + "disconnects": { + "type": "long" + }, + "total": { + "type": "long" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "properties": { + "overall": { + "properties": { + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + } + } + }, + "kibana_stats": { + "properties": { + "concurrent_connections": { + "path": "kibana.stats.concurrent_connections", + "type": "alias" + }, + "kibana": { + "properties": { + "response_time": { + "properties": { + "max": { + "path": "kibana.stats.response_time.max.ms", + "type": "alias" + } + } + }, + "status": { + "path": "kibana.stats.kibana.status", + "type": "alias" + }, + "uuid": { + "path": "service.id", + "type": "alias" + } + } + }, + "os": { + "properties": { + "load": { + "properties": { + "15m": { + "path": "kibana.stats.os.load.15m", + "type": "alias" + }, + "1m": { + "path": "kibana.stats.os.load.1m", + "type": "alias" + }, + "5m": { + "path": "kibana.stats.os.load.5m", + "type": "alias" + } + } + }, + "memory": { + "properties": { + "free_in_bytes": { + "path": "kibana.stats.os.memory.free_in_bytes", + "type": "alias" + } + } + } + } + }, + "process": { + "properties": { + "event_loop_delay": { + "path": "kibana.stats.process.event_loop_delay.ms", + "type": "alias" + }, + "memory": { + "properties": { + "heap": { + "properties": { + "size_limit": { + "path": "kibana.stats.process.memory.heap.size_limit.bytes", + "type": "alias" + } + } + }, + "resident_set_size_in_bytes": { + "path": "kibana.stats.process.memory.resident_set_size.bytes", + "type": "alias" + } + } + }, + "uptime_in_millis": { + "path": "kibana.stats.process.uptime.ms", + "type": "alias" + } + } + }, + "requests": { + "properties": { + "disconnects": { + "path": "kibana.stats.request.disconnects", + "type": "alias" + }, + "total": { + "path": "kibana.stats.request.total", + "type": "alias" + } + } + }, + "response_times": { + "properties": { + "average": { + "path": "kibana.stats.response_time.avg.ms", + "type": "alias" + }, + "max": { + "path": "kibana.stats.response_time.max.ms", + "type": "alias" + } + } + }, + "timestamp": { + "path": "@timestamp", + "type": "alias" + } + } + }, + "kubernetes": { + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "apiserver": { + "properties": { + "audit": { + "properties": { + "event": { + "properties": { + "count": { + "type": "long" + } + } + }, + "rejected": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "client": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "etcd": { + "properties": { + "object": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "process": { + "properties": { + "cpu": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "fds": { + "properties": { + "open": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "resident": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "virtual": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "started": { + "properties": { + "sec": { + "type": "double" + } + } + } + } + }, + "request": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "component": { + "ignore_above": 1024, + "type": "keyword" + }, + "content_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "count": { + "type": "long" + }, + "current": { + "properties": { + "count": { + "type": "long" + } + } + }, + "dry_run": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "properties": { + "us": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "handler": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "longrunning": { + "properties": { + "count": { + "type": "long" + } + } + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "ignore_above": 1024, + "type": "keyword" + }, + "scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "subresource": { + "ignore_above": 1024, + "type": "keyword" + }, + "verb": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "size": { + "properties": { + "bytes": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + }, + "watch": { + "properties": { + "events": { + "properties": { + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "properties": { + "bytes": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + } + } + } + } + }, + "container": { + "properties": { + "cpu": { + "properties": { + "limit": { + "properties": { + "cores": { + "type": "float" + } + } + }, + "request": { + "properties": { + "cores": { + "type": "float" + } + } + }, + "usage": { + "properties": { + "core": { + "properties": { + "ns": { + "type": "double" + } + } + }, + "limit": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "nanocores": { + "type": "double" + }, + "node": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "logs": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "capacity": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "inodes": { + "properties": { + "count": { + "type": "double" + }, + "free": { + "type": "double" + }, + "used": { + "type": "double" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "double" + } + } + } + } + }, + "memory": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "majorpagefaults": { + "type": "double" + }, + "pagefaults": { + "type": "double" + }, + "request": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "rss": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "double" + }, + "limit": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "node": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "workingset": { + "properties": { + "bytes": { + "type": "double" + }, + "limit": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "rootfs": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "capacity": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "inodes": { + "properties": { + "used": { + "type": "double" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "double" + } + } + } + } + }, + "start_time": { + "type": "date" + }, + "status": { + "properties": { + "last_terminated_reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "phase": { + "ignore_above": 1024, + "type": "keyword" + }, + "ready": { + "type": "boolean" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "restarts": { + "type": "long" + } + } + } + } + }, + "controllermanager": { + "properties": { + "client": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + }, + "duration": { + "properties": { + "us": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "leader": { + "properties": { + "is_master": { + "type": "boolean" + } + } + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "collector": { + "properties": { + "count": { + "type": "long" + }, + "eviction": { + "properties": { + "count": { + "type": "long" + } + } + }, + "health": { + "properties": { + "pct": { + "type": "long" + } + } + }, + "unhealthy": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "process": { + "properties": { + "cpu": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "fds": { + "properties": { + "max": { + "properties": { + "count": { + "type": "long" + } + } + }, + "open": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "resident": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "virtual": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "started": { + "properties": { + "sec": { + "type": "double" + } + } + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "verb": { + "ignore_above": 1024, + "type": "keyword" + }, + "workqueue": { + "properties": { + "adds": { + "properties": { + "count": { + "type": "long" + } + } + }, + "depth": { + "properties": { + "count": { + "type": "long" + } + } + }, + "longestrunning": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "retries": { + "properties": { + "count": { + "type": "long" + } + } + }, + "unfinished": { + "properties": { + "sec": { + "type": "double" + } + } + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cronjob": { + "properties": { + "active": { + "properties": { + "count": { + "type": "long" + } + } + }, + "concurrency": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "deadline": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "is_suspended": { + "type": "boolean" + }, + "last_schedule": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "next_schedule": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "schedule": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "daemonset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "replicas": { + "properties": { + "available": { + "type": "long" + }, + "desired": { + "type": "long" + }, + "ready": { + "type": "long" + }, + "unavailable": { + "type": "long" + } + } + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "paused": { + "type": "boolean" + }, + "replicas": { + "properties": { + "available": { + "type": "long" + }, + "desired": { + "type": "long" + }, + "unavailable": { + "type": "long" + }, + "updated": { + "type": "long" + } + } + } + } + }, + "event": { + "properties": { + "count": { + "type": "long" + }, + "involved_object": { + "properties": { + "api_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message": { + "copy_to": "message", + "norms": false, + "type": "text" + }, + "metadata": { + "properties": { + "generate_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "self_link": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "properties": { + "created": { + "type": "date" + } + } + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "source": { + "properties": { + "component": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timestamp": { + "properties": { + "first_occurrence": { + "type": "date" + }, + "last_occurrence": { + "type": "date" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "job": { + "properties": { + "completions": { + "properties": { + "desired": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "properties": { + "is_controller": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "parallelism": { + "properties": { + "desired": { + "type": "long" + } + } + }, + "pods": { + "properties": { + "active": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "succeeded": { + "type": "long" + } + } + }, + "status": { + "properties": { + "complete": { + "ignore_above": 1024, + "type": "keyword" + }, + "failed": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "time": { + "properties": { + "completed": { + "type": "date" + }, + "created": { + "type": "date" + } + } + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "cpu": { + "properties": { + "allocatable": { + "properties": { + "cores": { + "type": "float" + } + } + }, + "capacity": { + "properties": { + "cores": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "core": { + "properties": { + "ns": { + "type": "double" + } + } + }, + "nanocores": { + "type": "double" + } + } + } + } + }, + "fs": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "capacity": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "inodes": { + "properties": { + "count": { + "type": "double" + }, + "free": { + "type": "double" + }, + "used": { + "type": "double" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "double" + } + } + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory": { + "properties": { + "allocatable": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "capacity": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "majorpagefaults": { + "type": "double" + }, + "pagefaults": { + "type": "double" + }, + "rss": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "workingset": { + "properties": { + "bytes": { + "type": "double" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "rx": { + "properties": { + "bytes": { + "type": "double" + }, + "errors": { + "type": "double" + } + } + }, + "tx": { + "properties": { + "bytes": { + "type": "double" + }, + "errors": { + "type": "double" + } + } + } + } + }, + "pod": { + "properties": { + "allocatable": { + "properties": { + "total": { + "type": "long" + } + } + }, + "capacity": { + "properties": { + "total": { + "type": "long" + } + } + } + } + }, + "runtime": { + "properties": { + "imagefs": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "capacity": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "double" + } + } + } + } + } + } + }, + "start_time": { + "type": "date" + }, + "status": { + "properties": { + "disk_pressure": { + "ignore_above": 1024, + "type": "keyword" + }, + "memory_pressure": { + "ignore_above": 1024, + "type": "keyword" + }, + "out_of_disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid_pressure": { + "ignore_above": 1024, + "type": "keyword" + }, + "ready": { + "ignore_above": 1024, + "type": "keyword" + }, + "unschedulable": { + "type": "boolean" + } + } + } + } + }, + "persistentvolume": { + "properties": { + "capacity": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "phase": { + "ignore_above": 1024, + "type": "keyword" + }, + "storage_class": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "persistentvolumeclaim": { + "properties": { + "access_mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "phase": { + "ignore_above": 1024, + "type": "keyword" + }, + "request_storage": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "storage_class": { + "ignore_above": 1024, + "type": "keyword" + }, + "volume_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "cpu": { + "properties": { + "usage": { + "properties": { + "limit": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "nanocores": { + "type": "double" + }, + "node": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "host_ip": { + "type": "ip" + }, + "ip": { + "type": "ip" + }, + "memory": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "major_page_faults": { + "type": "double" + }, + "page_faults": { + "type": "double" + }, + "rss": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "double" + }, + "limit": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "node": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "working_set": { + "properties": { + "bytes": { + "type": "double" + }, + "limit": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network": { + "properties": { + "rx": { + "properties": { + "bytes": { + "type": "double" + }, + "errors": { + "type": "double" + } + } + }, + "tx": { + "properties": { + "bytes": { + "type": "double" + }, + "errors": { + "type": "double" + } + } + } + } + }, + "start_time": { + "type": "date" + }, + "status": { + "properties": { + "phase": { + "ignore_above": 1024, + "type": "keyword" + }, + "ready": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheduled": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "proxy": { + "properties": { + "client": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "handler": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "http": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + }, + "duration": { + "properties": { + "us": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "double" + } + } + } + } + }, + "size": { + "properties": { + "bytes": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "long" + } + } + } + } + } + } + }, + "response": { + "properties": { + "size": { + "properties": { + "bytes": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "process": { + "properties": { + "cpu": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "fds": { + "properties": { + "open": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "resident": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "virtual": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "started": { + "properties": { + "sec": { + "type": "double" + } + } + } + } + }, + "sync": { + "properties": { + "networkprogramming": { + "properties": { + "duration": { + "properties": { + "us": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + }, + "rules": { + "properties": { + "duration": { + "properties": { + "us": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + } + } + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "replicas": { + "properties": { + "available": { + "type": "long" + }, + "desired": { + "type": "long" + }, + "labeled": { + "type": "long" + }, + "observed": { + "type": "long" + }, + "ready": { + "type": "long" + } + } + } + } + }, + "resourcequota": { + "properties": { + "created": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "quota": { + "type": "double" + }, + "resource": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scheduler": { + "properties": { + "client": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "handler": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "http": { + "properties": { + "request": { + "properties": { + "count": { + "type": "long" + }, + "duration": { + "properties": { + "us": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "double" + } + } + } + } + }, + "size": { + "properties": { + "bytes": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "long" + } + } + } + } + } + } + }, + "response": { + "properties": { + "size": { + "properties": { + "bytes": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "leader": { + "properties": { + "is_master": { + "type": "boolean" + } + } + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "operation": { + "ignore_above": 1024, + "type": "keyword" + }, + "process": { + "properties": { + "cpu": { + "properties": { + "sec": { + "type": "double" + } + } + }, + "fds": { + "properties": { + "open": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "resident": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "virtual": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "started": { + "properties": { + "sec": { + "type": "double" + } + } + } + } + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheduling": { + "properties": { + "duration": { + "properties": { + "seconds": { + "properties": { + "count": { + "type": "long" + }, + "percentile": { + "properties": { + "*": { + "type": "object" + } + } + }, + "sum": { + "type": "double" + } + } + } + } + }, + "e2e": { + "properties": { + "duration": { + "properties": { + "us": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "object" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + }, + "pod": { + "properties": { + "attempts": { + "properties": { + "count": { + "type": "long" + } + } + }, + "preemption": { + "properties": { + "victims": { + "properties": { + "bucket": { + "properties": { + "*": { + "type": "long" + } + } + }, + "count": { + "type": "long" + }, + "sum": { + "type": "long" + } + } + } + } + } + } + } + } + } + } + }, + "selectors": { + "properties": { + "*": { + "type": "object" + } + } + }, + "service": { + "properties": { + "cluster_ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "external_ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "external_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress_hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress_ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "load_balancer_ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "created": { + "type": "long" + }, + "generation": { + "properties": { + "desired": { + "type": "long" + }, + "observed": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "replicas": { + "properties": { + "desired": { + "type": "long" + }, + "observed": { + "type": "long" + }, + "ready": { + "type": "long" + } + } + } + } + }, + "storageclass": { + "properties": { + "created": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "provisioner": { + "ignore_above": 1024, + "type": "keyword" + }, + "reclaim_policy": { + "ignore_above": 1024, + "type": "keyword" + }, + "volume_binding_mode": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "system": { + "properties": { + "container": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu": { + "properties": { + "usage": { + "properties": { + "core": { + "properties": { + "ns": { + "type": "double" + } + } + }, + "nanocores": { + "type": "double" + } + } + } + } + }, + "memory": { + "properties": { + "majorpagefaults": { + "type": "double" + }, + "pagefaults": { + "type": "double" + }, + "rss": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "workingset": { + "properties": { + "bytes": { + "type": "double" + } + } + } + } + }, + "start_time": { + "type": "date" + } + } + }, + "volume": { + "properties": { + "fs": { + "properties": { + "available": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "capacity": { + "properties": { + "bytes": { + "type": "double" + } + } + }, + "inodes": { + "properties": { + "count": { + "type": "double" + }, + "free": { + "type": "double" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "used": { + "type": "double" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "double" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "kvm": { + "properties": { + "dommemstat": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "stat": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "type": "long" + } + } + } + } + }, + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "properties": { + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "license": { + "properties": { + "status": { + "path": "elasticsearch.cluster.stats.license.status", + "type": "alias" + }, + "type": { + "path": "elasticsearch.cluster.stats.license.type", + "type": "alias" + } + } + }, + "linux": { + "properties": { + "conntrack": { + "properties": { + "summary": { + "properties": { + "drop": { + "type": "long" + }, + "early_drop": { + "type": "long" + }, + "entries": { + "type": "long" + }, + "found": { + "type": "long" + }, + "ignore": { + "type": "long" + }, + "insert_failed": { + "type": "long" + }, + "invalid": { + "type": "long" + }, + "search_restart": { + "type": "long" + } + } + } + } + }, + "iostat": { + "properties": { + "await": { + "type": "float" + }, + "busy": { + "type": "float" + }, + "queue": { + "properties": { + "avg_size": { + "type": "float" + } + } + }, + "read": { + "properties": { + "await": { + "type": "float" + }, + "per_sec": { + "properties": { + "bytes": { + "type": "float" + } + } + }, + "request": { + "properties": { + "merges_per_sec": { + "type": "float" + }, + "per_sec": { + "type": "float" + } + } + } + } + }, + "request": { + "properties": { + "avg_size": { + "type": "float" + } + } + }, + "service_time": { + "type": "float" + }, + "write": { + "properties": { + "await": { + "type": "float" + }, + "per_sec": { + "properties": { + "bytes": { + "type": "float" + } + } + }, + "request": { + "properties": { + "merges_per_sec": { + "type": "float" + }, + "per_sec": { + "type": "float" + } + } + } + } + } + } + }, + "ksm": { + "properties": { + "stats": { + "properties": { + "full_scans": { + "type": "long" + }, + "pages_shared": { + "type": "long" + }, + "pages_sharing": { + "type": "long" + }, + "pages_unshared": { + "type": "long" + }, + "stable_node_chains": { + "type": "long" + }, + "stable_node_dups": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "hugepages": { + "properties": { + "default_size": { + "type": "long" + }, + "free": { + "type": "long" + }, + "reserved": { + "type": "long" + }, + "surplus": { + "type": "long" + }, + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "type": "long" + } + } + } + } + }, + "page_stats": { + "properties": { + "direct_efficiency": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "kswapd_efficiency": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pgfree": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "pgscan_direct": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "pgscan_kswapd": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "pgsteal_direct": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "pgsteal_kswapd": { + "properties": { + "pages": { + "type": "long" + } + } + } + } + }, + "swap": { + "properties": { + "free": { + "type": "long" + }, + "in": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "out": { + "properties": { + "pages": { + "type": "long" + } + } + }, + "readahead": { + "properties": { + "cached": { + "type": "long" + }, + "pages": { + "type": "long" + } + } + }, + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + } + } + }, + "pageinfo": { + "properties": { + "buddy_info": { + "properties": { + "DMA": { + "properties": { + "0": { + "type": "long" + }, + "1": { + "type": "long" + }, + "10": { + "type": "long" + }, + "2": { + "type": "long" + }, + "3": { + "type": "long" + }, + "4": { + "type": "long" + }, + "5": { + "type": "long" + }, + "6": { + "type": "long" + }, + "7": { + "type": "long" + }, + "8": { + "type": "long" + }, + "9": { + "type": "long" + } + } + } + } + }, + "nodes": { + "properties": { + "*": { + "type": "object" + } + } + } + } + }, + "pressure": { + "properties": { + "cpu": { + "properties": { + "some": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "properties": { + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "io": { + "properties": { + "full": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "properties": { + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + }, + "some": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "properties": { + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "memory": { + "properties": { + "full": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "properties": { + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + }, + "some": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "properties": { + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + } + } + } + } + }, + "rapl": { + "properties": { + "core": { + "type": "long" + }, + "dram": { + "properties": { + "joules": { + "type": "float" + }, + "watts": { + "type": "float" + } + } + }, + "package": { + "properties": { + "joules": { + "type": "float" + }, + "watts": { + "type": "float" + } + } + }, + "pp0": { + "properties": { + "joules": { + "type": "float" + }, + "watts": { + "type": "float" + } + } + }, + "pp1": { + "properties": { + "joules": { + "type": "float" + }, + "watts": { + "type": "float" + } + } + } + } + } + } + }, + "log": { + "properties": { + "file": { + "properties": { + "path": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "object" + } + } + }, + "logstash": { + "properties": { + "elasticsearch": { + "properties": { + "cluster": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "node": { + "properties": { + "jvm": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "properties": { + "pipeline": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "stats": { + "properties": { + "events": { + "properties": { + "duration_in_millis": { + "type": "long" + }, + "filtered": { + "type": "long" + }, + "in": { + "type": "long" + }, + "out": { + "type": "long" + } + } + }, + "jvm": { + "properties": { + "mem": { + "properties": { + "heap_max_in_bytes": { + "type": "long" + }, + "heap_used_in_bytes": { + "type": "long" + } + } + }, + "uptime_in_millis": { + "type": "long" + } + } + }, + "logstash": { + "properties": { + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "cgroup": { + "properties": { + "cpu": { + "properties": { + "stat": { + "properties": { + "number_of_elapsed_periods": { + "type": "long" + }, + "number_of_times_throttled": { + "type": "long" + }, + "time_throttled_nanos": { + "type": "long" + } + } + } + } + }, + "cpuacct": { + "properties": { + "usage_nanos": { + "type": "long" + } + } + } + } + }, + "cpu": { + "properties": { + "load_average": { + "properties": { + "15m": { + "type": "long" + }, + "1m": { + "type": "long" + }, + "5m": { + "type": "long" + } + } + } + } + } + } + }, + "pipelines": { + "properties": { + "events": { + "properties": { + "duration_in_millis": { + "type": "long" + }, + "out": { + "type": "long" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "queue": { + "properties": { + "events_count": { + "type": "long" + }, + "max_queue_size_in_bytes": { + "type": "long" + }, + "queue_size_in_bytes": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vertices": { + "properties": { + "duration_in_millis": { + "type": "long" + }, + "events_in": { + "type": "long" + }, + "events_out": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "pipeline_ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "queue_push_duration_in_millis": { + "type": "float" + } + } + } + }, + "type": "nested" + }, + "process": { + "properties": { + "cpu": { + "properties": { + "percent": { + "type": "double" + } + } + } + } + }, + "queue": { + "properties": { + "events_count": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "logstash_state": { + "properties": { + "pipeline": { + "properties": { + "hash": { + "path": "logstash.node.state.pipeline.hash", + "type": "alias" + }, + "id": { + "path": "logstash.node.state.pipeline.id", + "type": "alias" + } + } + } + } + }, + "logstash_stats": { + "properties": { + "events": { + "properties": { + "duration_in_millis": { + "path": "logstash.node.stats.events.duration_in_millis", + "type": "alias" + }, + "in": { + "path": "logstash.node.stats.events.in", + "type": "alias" + }, + "out": { + "path": "logstash.node.stats.events.out", + "type": "alias" + } + } + }, + "jvm": { + "properties": { + "mem": { + "properties": { + "heap_max_in_bytes": { + "path": "logstash.node.stats.jvm.mem.heap_max_in_bytes", + "type": "alias" + }, + "heap_used_in_bytes": { + "path": "logstash.node.stats.jvm.mem.heap_used_in_bytes", + "type": "alias" + } + } + }, + "uptime_in_millis": { + "path": "logstash.node.stats.jvm.uptime_in_millis", + "type": "alias" + } + } + }, + "logstash": { + "properties": { + "uuid": { + "path": "logstash.node.stats.logstash.uuid", + "type": "alias" + }, + "version": { + "path": "logstash.node.stats.logstash.version", + "type": "alias" + } + } + }, + "os": { + "properties": { + "cgroup": { + "properties": { + "cpuacct": { + "properties": { + "usage_nanos": { + "path": "logstash.node.stats.os.cgroup.cpuacct.usage_nanos", + "type": "alias" + } + } + } + } + }, + "cpu": { + "properties": { + "load_average": { + "properties": { + "15m": { + "path": "logstash.node.stats.os.cpu.load_average.15m", + "type": "alias" + }, + "1m": { + "path": "logstash.node.stats.os.cpu.load_average.1m", + "type": "alias" + }, + "5m": { + "path": "logstash.node.stats.os.cpu.load_average.5m", + "type": "alias" + } + } + }, + "stat": { + "properties": { + "number_of_elapsed_periods": { + "path": "logstash.node.stats.os.cgroup.cpu.stat.number_of_elapsed_periods", + "type": "alias" + }, + "number_of_times_throttled": { + "path": "logstash.node.stats.os.cgroup.cpu.stat.number_of_times_throttled", + "type": "alias" + }, + "time_throttled_nanos": { + "path": "logstash.node.stats.os.cgroup.cpu.stat.time_throttled_nanos", + "type": "alias" + } + } + } + } + } + } + }, + "pipelines": { + "type": "nested" + }, + "process": { + "properties": { + "cpu": { + "properties": { + "percent": { + "path": "logstash.node.stats.process.cpu.percent", + "type": "alias" + } + } + } + } + }, + "queue": { + "properties": { + "events_count": { + "path": "logstash.node.stats.queue.events_count", + "type": "alias" + } + } + }, + "timestamp": { + "path": "@timestamp", + "type": "alias" + } + } + }, + "memcached": { + "properties": { + "stats": { + "properties": { + "bytes": { + "properties": { + "current": { + "type": "long" + }, + "limit": { + "type": "long" + } + } + }, + "cmd": { + "properties": { + "get": { + "type": "long" + }, + "set": { + "type": "long" + } + } + }, + "connections": { + "properties": { + "current": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "evictions": { + "type": "long" + }, + "get": { + "properties": { + "hits": { + "type": "long" + }, + "misses": { + "type": "long" + } + } + }, + "items": { + "properties": { + "current": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "pid": { + "type": "long" + }, + "read": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "threads": { + "type": "long" + }, + "uptime": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "written": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "metricset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "period": { + "type": "long" + } + } + }, + "mongodb": { + "properties": { + "collstats": { + "properties": { + "collection": { + "ignore_above": 1024, + "type": "keyword" + }, + "commands": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "db": { + "ignore_above": 1024, + "type": "keyword" + }, + "getmore": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "insert": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "lock": { + "properties": { + "read": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "write": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "queries": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "remove": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "total": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "update": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + } + } + }, + "dbstats": { + "properties": { + "avg_obj_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "collections": { + "type": "long" + }, + "data_file_version": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + } + } + }, + "data_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "db": { + "ignore_above": 1024, + "type": "keyword" + }, + "extent_free_list": { + "properties": { + "num": { + "type": "long" + }, + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "file_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "index_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "indexes": { + "type": "long" + }, + "ns_size_mb": { + "properties": { + "mb": { + "type": "long" + } + } + }, + "num_extents": { + "type": "long" + }, + "objects": { + "type": "long" + }, + "storage_size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "metrics": { + "properties": { + "commands": { + "properties": { + "aggregate": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "build_info": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "coll_stats": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "connection_pool_stats": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "count": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "db_stats": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "distinct": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "find": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "get_cmd_line_opts": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "get_last_error": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "get_log": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "get_more": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "get_parameter": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "host_info": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "insert": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "is_master": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "is_self": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "last_collections": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "last_commands": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "list_databased": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "list_indexes": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "ping": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "profile": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "replset_get_rbid": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "replset_get_status": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "replset_heartbeat": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "replset_update_position": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "server_status": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "update": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "whatsmyuri": { + "properties": { + "failed": { + "type": "long" + }, + "total": { + "type": "long" + } + } + } + } + }, + "cursor": { + "properties": { + "open": { + "properties": { + "no_timeout": { + "type": "long" + }, + "pinned": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "timed_out": { + "type": "long" + } + } + }, + "document": { + "properties": { + "deleted": { + "type": "long" + }, + "inserted": { + "type": "long" + }, + "returned": { + "type": "long" + }, + "updated": { + "type": "long" + } + } + }, + "get_last_error": { + "properties": { + "write_timeouts": { + "type": "long" + }, + "write_wait": { + "properties": { + "count": { + "type": "long" + }, + "ms": { + "type": "long" + } + } + } + } + }, + "operation": { + "properties": { + "scan_and_order": { + "type": "long" + }, + "write_conflicts": { + "type": "long" + } + } + }, + "query_executor": { + "properties": { + "scanned_documents": { + "properties": { + "count": { + "type": "long" + } + } + }, + "scanned_indexes": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "replication": { + "properties": { + "apply": { + "properties": { + "attempts_to_become_secondary": { + "type": "long" + }, + "batches": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "ops": { + "type": "long" + } + } + }, + "buffer": { + "properties": { + "count": { + "type": "long" + }, + "max_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "executor": { + "properties": { + "counters": { + "properties": { + "cancels": { + "type": "long" + }, + "event_created": { + "type": "long" + }, + "event_wait": { + "type": "long" + }, + "scheduled": { + "properties": { + "dbwork": { + "type": "long" + }, + "exclusive": { + "type": "long" + }, + "failures": { + "type": "long" + }, + "netcmd": { + "type": "long" + }, + "work": { + "type": "long" + }, + "work_at": { + "type": "long" + } + } + }, + "waits": { + "type": "long" + } + } + }, + "event_waiters": { + "type": "long" + }, + "network_interface": { + "ignore_above": 1024, + "type": "keyword" + }, + "queues": { + "properties": { + "free": { + "type": "long" + }, + "in_progress": { + "properties": { + "dbwork": { + "type": "long" + }, + "exclusive": { + "type": "long" + }, + "network": { + "type": "long" + } + } + }, + "ready": { + "type": "long" + }, + "sleepers": { + "type": "long" + } + } + }, + "shutting_down": { + "type": "boolean" + }, + "unsignaled_events": { + "type": "long" + } + } + }, + "initial_sync": { + "properties": { + "completed": { + "type": "long" + }, + "failed_attempts": { + "type": "long" + }, + "failures": { + "type": "long" + } + } + }, + "network": { + "properties": { + "bytes": { + "type": "long" + }, + "getmores": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "ops": { + "type": "long" + }, + "reders_created": { + "type": "long" + } + } + }, + "preload": { + "properties": { + "docs": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "indexes": { + "properties": { + "count": { + "type": "long" + }, + "time": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "storage": { + "properties": { + "free_list": { + "properties": { + "search": { + "properties": { + "bucket_exhausted": { + "type": "long" + }, + "requests": { + "type": "long" + }, + "scanned": { + "type": "long" + } + } + } + } + } + } + }, + "ttl": { + "properties": { + "deleted_documents": { + "properties": { + "count": { + "type": "long" + } + } + }, + "passes": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "replstatus": { + "properties": { + "headroom": { + "properties": { + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "lag": { + "properties": { + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "members": { + "properties": { + "arbiter": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "down": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "primary": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "optime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "recovering": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rollback": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secondary": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + }, + "optimes": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "startup2": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "unhealthy": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "unknown": { + "properties": { + "count": { + "type": "long" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "oplog": { + "properties": { + "first": { + "properties": { + "timestamp": { + "type": "long" + } + } + }, + "last": { + "properties": { + "timestamp": { + "type": "long" + } + } + }, + "size": { + "properties": { + "allocated": { + "type": "long" + }, + "used": { + "type": "long" + } + } + }, + "window": { + "type": "long" + } + } + }, + "optimes": { + "properties": { + "applied": { + "type": "long" + }, + "durable": { + "type": "long" + }, + "last_committed": { + "type": "long" + } + } + }, + "server_date": { + "type": "date" + }, + "set_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "status": { + "properties": { + "asserts": { + "properties": { + "msg": { + "type": "long" + }, + "regular": { + "type": "long" + }, + "rollovers": { + "type": "long" + }, + "user": { + "type": "long" + }, + "warning": { + "type": "long" + } + } + }, + "background_flushing": { + "properties": { + "average": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "flushes": { + "type": "long" + }, + "last": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "last_finished": { + "type": "date" + }, + "total": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "connections": { + "properties": { + "available": { + "type": "long" + }, + "current": { + "type": "long" + }, + "total_created": { + "type": "long" + } + } + }, + "extra_info": { + "properties": { + "heap_usage": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "page_faults": { + "type": "long" + } + } + }, + "global_lock": { + "properties": { + "active_clients": { + "properties": { + "readers": { + "type": "long" + }, + "total": { + "type": "long" + }, + "writers": { + "type": "long" + } + } + }, + "current_queue": { + "properties": { + "readers": { + "type": "long" + }, + "total": { + "type": "long" + }, + "writers": { + "type": "long" + } + } + }, + "total_time": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "journaling": { + "properties": { + "commits": { + "type": "long" + }, + "commits_in_write_lock": { + "type": "long" + }, + "compression": { + "type": "long" + }, + "early_commits": { + "type": "long" + }, + "journaled": { + "properties": { + "mb": { + "type": "long" + } + } + }, + "times": { + "properties": { + "commits": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "commits_in_write_lock": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "dt": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "prep_log_buffer": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "remap_private_view": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "write_to_data_files": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "write_to_journal": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "write_to_data_files": { + "properties": { + "mb": { + "type": "long" + } + } + } + } + }, + "local_time": { + "type": "date" + }, + "locks": { + "properties": { + "collection": { + "properties": { + "acquire": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "deadlock": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "wait": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + }, + "us": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + } + } + }, + "database": { + "properties": { + "acquire": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "deadlock": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "wait": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + }, + "us": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + } + } + }, + "global": { + "properties": { + "acquire": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "deadlock": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "wait": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + }, + "us": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + } + } + }, + "meta_data": { + "properties": { + "acquire": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "deadlock": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "wait": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + }, + "us": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + } + } + }, + "oplog": { + "properties": { + "acquire": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "deadlock": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + }, + "wait": { + "properties": { + "count": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + }, + "us": { + "properties": { + "R": { + "type": "long" + }, + "W": { + "type": "long" + }, + "r": { + "type": "long" + }, + "w": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "memory": { + "properties": { + "bits": { + "type": "long" + }, + "mapped": { + "properties": { + "mb": { + "type": "long" + } + } + }, + "mapped_with_journal": { + "properties": { + "mb": { + "type": "long" + } + } + }, + "resident": { + "properties": { + "mb": { + "type": "long" + } + } + }, + "virtual": { + "properties": { + "mb": { + "type": "long" + } + } + } + } + }, + "network": { + "properties": { + "in": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "out": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "requests": { + "type": "long" + } + } + }, + "ops": { + "properties": { + "counters": { + "properties": { + "command": { + "type": "long" + }, + "delete": { + "type": "long" + }, + "getmore": { + "type": "long" + }, + "insert": { + "type": "long" + }, + "query": { + "type": "long" + }, + "update": { + "type": "long" + } + } + }, + "latencies": { + "properties": { + "commands": { + "properties": { + "count": { + "type": "long" + }, + "latency": { + "type": "long" + } + } + }, + "reads": { + "properties": { + "count": { + "type": "long" + }, + "latency": { + "type": "long" + } + } + }, + "writes": { + "properties": { + "count": { + "type": "long" + }, + "latency": { + "type": "long" + } + } + } + } + }, + "replicated": { + "properties": { + "command": { + "type": "long" + }, + "delete": { + "type": "long" + }, + "getmore": { + "type": "long" + }, + "insert": { + "type": "long" + }, + "query": { + "type": "long" + }, + "update": { + "type": "long" + } + } + } + } + }, + "process": { + "path": "process.name", + "type": "alias" + }, + "storage_engine": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uptime": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "version": { + "path": "service.version", + "type": "alias" + }, + "wired_tiger": { + "properties": { + "cache": { + "properties": { + "dirty": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "maximum": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "pages": { + "properties": { + "evicted": { + "type": "long" + }, + "read": { + "type": "long" + }, + "write": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "concurrent_transactions": { + "properties": { + "read": { + "properties": { + "available": { + "type": "long" + }, + "out": { + "type": "long" + }, + "total_tickets": { + "type": "long" + } + } + }, + "write": { + "properties": { + "available": { + "type": "long" + }, + "out": { + "type": "long" + }, + "total_tickets": { + "type": "long" + } + } + } + } + }, + "log": { + "properties": { + "flushes": { + "type": "long" + }, + "max_file_size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "scans": { + "type": "long" + }, + "size": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "syncs": { + "type": "long" + }, + "write": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "writes": { + "type": "long" + } + } + } + } + }, + "write_backs_queued": { + "type": "boolean" + } + } + } + } + }, + "munin": { + "properties": { + "metrics": { + "properties": { + "*": { + "type": "object" + } + } + }, + "plugin": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "mysql": { + "properties": { + "galera_status": { + "properties": { + "apply": { + "properties": { + "oooe": { + "type": "double" + }, + "oool": { + "type": "double" + }, + "window": { + "type": "double" + } + } + }, + "cert": { + "properties": { + "deps_distance": { + "type": "double" + }, + "index_size": { + "type": "long" + }, + "interval": { + "type": "double" + } + } + }, + "cluster": { + "properties": { + "conf_id": { + "type": "long" + }, + "size": { + "type": "long" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "commit": { + "properties": { + "oooe": { + "type": "double" + }, + "window": { + "type": "long" + } + } + }, + "connected": { + "ignore_above": 1024, + "type": "keyword" + }, + "evs": { + "properties": { + "evict": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "flow_ctl": { + "properties": { + "paused": { + "type": "double" + }, + "paused_ns": { + "type": "long" + }, + "recv": { + "type": "long" + }, + "sent": { + "type": "long" + } + } + }, + "last_committed": { + "type": "long" + }, + "local": { + "properties": { + "bf_aborts": { + "type": "long" + }, + "cert_failures": { + "type": "long" + }, + "commits": { + "type": "long" + }, + "recv": { + "properties": { + "queue": { + "type": "long" + }, + "queue_avg": { + "type": "double" + }, + "queue_max": { + "type": "long" + }, + "queue_min": { + "type": "long" + } + } + }, + "replays": { + "type": "long" + }, + "send": { + "properties": { + "queue": { + "type": "long" + }, + "queue_avg": { + "type": "double" + }, + "queue_max": { + "type": "long" + }, + "queue_min": { + "type": "long" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ready": { + "ignore_above": 1024, + "type": "keyword" + }, + "received": { + "properties": { + "bytes": { + "type": "long" + }, + "count": { + "type": "long" + } + } + }, + "repl": { + "properties": { + "bytes": { + "type": "long" + }, + "count": { + "type": "long" + }, + "data_bytes": { + "type": "long" + }, + "keys": { + "type": "long" + }, + "keys_bytes": { + "type": "long" + }, + "other_bytes": { + "type": "long" + } + } + } + } + }, + "performance": { + "properties": { + "events_statements": { + "properties": { + "avg": { + "properties": { + "timer": { + "properties": { + "wait": { + "type": "long" + } + } + } + } + }, + "count": { + "properties": { + "star": { + "type": "long" + } + } + }, + "digest": { + "norms": false, + "type": "text" + }, + "last": { + "properties": { + "seen": { + "type": "date" + } + } + }, + "max": { + "properties": { + "timer": { + "properties": { + "wait": { + "type": "long" + } + } + } + } + }, + "quantile": { + "properties": { + "95": { + "type": "long" + } + } + } + } + }, + "table_io_waits": { + "properties": { + "count": { + "properties": { + "fetch": { + "type": "long" + } + } + }, + "index": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "object": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "schema": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "status": { + "properties": { + "aborted": { + "properties": { + "clients": { + "type": "long" + }, + "connects": { + "type": "long" + } + } + }, + "binlog": { + "properties": { + "cache": { + "properties": { + "disk_use": { + "type": "long" + }, + "use": { + "type": "long" + } + } + } + } + }, + "bytes": { + "properties": { + "received": { + "type": "long" + }, + "sent": { + "type": "long" + } + } + }, + "cache": { + "properties": { + "ssl": { + "properties": { + "hits": { + "type": "long" + }, + "misses": { + "type": "long" + }, + "size": { + "type": "long" + } + } + }, + "table": { + "properties": { + "open_cache": { + "properties": { + "hits": { + "type": "long" + }, + "misses": { + "type": "long" + }, + "overflows": { + "type": "long" + } + } + } + } + } + } + }, + "command": { + "properties": { + "delete": { + "type": "long" + }, + "insert": { + "type": "long" + }, + "select": { + "type": "long" + }, + "update": { + "type": "long" + } + } + }, + "connection": { + "properties": { + "errors": { + "properties": { + "accept": { + "type": "long" + }, + "internal": { + "type": "long" + }, + "max": { + "type": "long" + }, + "peer_address": { + "type": "long" + }, + "select": { + "type": "long" + }, + "tcpwrap": { + "type": "long" + } + } + } + } + }, + "connections": { + "type": "long" + }, + "created": { + "properties": { + "tmp": { + "properties": { + "disk_tables": { + "type": "long" + }, + "files": { + "type": "long" + }, + "tables": { + "type": "long" + } + } + } + } + }, + "delayed": { + "properties": { + "errors": { + "type": "long" + }, + "insert_threads": { + "type": "long" + }, + "writes": { + "type": "long" + } + } + }, + "flush_commands": { + "type": "long" + }, + "handler": { + "properties": { + "commit": { + "type": "long" + }, + "delete": { + "type": "long" + }, + "external_lock": { + "type": "long" + }, + "mrr_init": { + "type": "long" + }, + "prepare": { + "type": "long" + }, + "read": { + "properties": { + "first": { + "type": "long" + }, + "key": { + "type": "long" + }, + "last": { + "type": "long" + }, + "next": { + "type": "long" + }, + "prev": { + "type": "long" + }, + "rnd": { + "type": "long" + }, + "rnd_next": { + "type": "long" + } + } + }, + "rollback": { + "type": "long" + }, + "savepoint": { + "type": "long" + }, + "savepoint_rollback": { + "type": "long" + }, + "update": { + "type": "long" + }, + "write": { + "type": "long" + } + } + }, + "innodb": { + "properties": { + "buffer_pool": { + "properties": { + "bytes": { + "properties": { + "data": { + "type": "long" + }, + "dirty": { + "type": "long" + } + } + }, + "dump_status": { + "type": "long" + }, + "load_status": { + "type": "long" + }, + "pages": { + "properties": { + "data": { + "type": "long" + }, + "dirty": { + "type": "long" + }, + "flushed": { + "type": "long" + }, + "free": { + "type": "long" + }, + "latched": { + "type": "long" + }, + "misc": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "pool": { + "properties": { + "reads": { + "type": "long" + }, + "resize_status": { + "type": "long" + }, + "wait_free": { + "type": "long" + } + } + }, + "read": { + "properties": { + "ahead": { + "type": "long" + }, + "ahead_evicted": { + "type": "long" + }, + "ahead_rnd": { + "type": "long" + }, + "requests": { + "type": "long" + } + } + }, + "write_requests": { + "type": "long" + } + } + }, + "rows": { + "properties": { + "deleted": { + "type": "long" + }, + "inserted": { + "type": "long" + }, + "reads": { + "type": "long" + }, + "updated": { + "type": "long" + } + } + } + } + }, + "max_used_connections": { + "type": "long" + }, + "open": { + "properties": { + "files": { + "type": "long" + }, + "streams": { + "type": "long" + }, + "tables": { + "type": "long" + } + } + }, + "opened_tables": { + "type": "long" + }, + "queries": { + "type": "long" + }, + "questions": { + "type": "long" + }, + "threads": { + "properties": { + "cached": { + "type": "long" + }, + "connected": { + "type": "long" + }, + "created": { + "type": "long" + }, + "running": { + "type": "long" + } + } + } + } + } + } + }, + "nats": { + "properties": { + "connection": { + "properties": { + "idle_time": { + "type": "long" + }, + "in": { + "properties": { + "bytes": { + "type": "long" + }, + "messages": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "out": { + "properties": { + "bytes": { + "type": "long" + }, + "messages": { + "type": "long" + } + } + }, + "pending_bytes": { + "type": "long" + }, + "subscriptions": { + "type": "long" + }, + "uptime": { + "type": "long" + } + } + }, + "connections": { + "properties": { + "total": { + "type": "long" + } + } + }, + "route": { + "properties": { + "in": { + "properties": { + "bytes": { + "type": "long" + }, + "messages": { + "type": "long" + } + } + }, + "ip": { + "type": "ip" + }, + "out": { + "properties": { + "bytes": { + "type": "long" + }, + "messages": { + "type": "long" + } + } + }, + "pending_size": { + "type": "long" + }, + "port": { + "type": "long" + }, + "remote_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "subscriptions": { + "type": "long" + } + } + }, + "routes": { + "properties": { + "total": { + "type": "long" + } + } + }, + "server": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "time": { + "type": "date" + } + } + }, + "stats": { + "properties": { + "cores": { + "type": "long" + }, + "cpu": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "http": { + "properties": { + "req_stats": { + "properties": { + "uri": { + "properties": { + "connz": { + "type": "long" + }, + "root": { + "type": "long" + }, + "routez": { + "type": "long" + }, + "subsz": { + "type": "long" + }, + "varz": { + "type": "long" + } + } + } + } + } + } + }, + "in": { + "properties": { + "bytes": { + "type": "long" + }, + "messages": { + "type": "long" + } + } + }, + "mem": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "out": { + "properties": { + "bytes": { + "type": "long" + }, + "messages": { + "type": "long" + } + } + }, + "remotes": { + "type": "long" + }, + "slow_consumers": { + "type": "long" + }, + "total_connections": { + "type": "long" + }, + "uptime": { + "type": "long" + } + } + }, + "subscriptions": { + "properties": { + "cache": { + "properties": { + "fanout": { + "properties": { + "avg": { + "type": "double" + }, + "max": { + "type": "long" + } + } + }, + "hit_rate": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "size": { + "type": "long" + } + } + }, + "inserts": { + "type": "long" + }, + "matches": { + "type": "long" + }, + "removes": { + "type": "long" + }, + "total": { + "type": "long" + } + } + } + } + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "nginx": { + "properties": { + "stubstatus": { + "properties": { + "accepts": { + "type": "long" + }, + "active": { + "type": "long" + }, + "current": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "handled": { + "type": "long" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "reading": { + "type": "long" + }, + "requests": { + "type": "long" + }, + "waiting": { + "type": "long" + }, + "writing": { + "type": "long" + } + } + } + } + }, + "node_stats": { + "properties": { + "fs": { + "properties": { + "io_stats": { + "properties": { + "total": { + "properties": { + "operations": { + "path": "elasticsearch.node.stats.fs.io_stats.total.operations.count", + "type": "alias" + }, + "read_operations": { + "path": "elasticsearch.node.stats.fs.io_stats.total.read.operations.count", + "type": "alias" + }, + "write_operations": { + "path": "elasticsearch.node.stats.fs.io_stats.total.write.operations.count", + "type": "alias" + } + } + } + } + }, + "summary": { + "properties": { + "available": { + "properties": { + "bytes": { + "path": "elasticsearch.node.stats.fs.summary.available.bytes", + "type": "alias" + } + } + }, + "total": { + "properties": { + "bytes": { + "path": "elasticsearch.node.stats.fs.summary.total.bytes", + "type": "alias" + } + } + } + } + }, + "total": { + "properties": { + "available_in_bytes": { + "path": "elasticsearch.node.stats.fs.summary.available.bytes", + "type": "alias" + }, + "total_in_bytes": { + "path": "elasticsearch.node.stats.fs.summary.total.bytes", + "type": "alias" + } + } + } + } + }, + "indices": { + "properties": { + "docs": { + "properties": { + "count": { + "path": "elasticsearch.node.stats.indices.docs.count", + "type": "alias" + } + } + }, + "fielddata": { + "properties": { + "memory_size_in_bytes": { + "path": "elasticsearch.node.stats.indices.fielddata.memory.bytes", + "type": "alias" + } + } + }, + "indexing": { + "properties": { + "index_time_in_millis": { + "path": "elasticsearch.node.stats.indices.indexing.index_time.ms", + "type": "alias" + }, + "index_total": { + "path": "elasticsearch.node.stats.indices.indexing.index_total.count", + "type": "alias" + }, + "throttle_time_in_millis": { + "path": "elasticsearch.node.stats.indices.indexing.throttle_time.ms", + "type": "alias" + } + } + }, + "query_cache": { + "properties": { + "memory_size_in_bytes": { + "path": "elasticsearch.node.stats.indices.query_cache.memory.bytes", + "type": "alias" + } + } + }, + "request_cache": { + "properties": { + "memory_size_in_bytes": { + "path": "elasticsearch.node.stats.indices.request_cache.memory.bytes", + "type": "alias" + } + } + }, + "search": { + "properties": { + "query_time_in_millis": { + "path": "elasticsearch.node.stats.indices.search.query_time.ms", + "type": "alias" + }, + "query_total": { + "path": "elasticsearch.node.stats.indices.search.query_total.count", + "type": "alias" + } + } + }, + "segments": { + "properties": { + "count": { + "path": "elasticsearch.node.stats.indices.segments.count", + "type": "alias" + }, + "doc_values_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.doc_values.memory.bytes", + "type": "alias" + }, + "fixed_bit_set_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.fixed_bit_set.memory.bytes", + "type": "alias" + }, + "index_writer_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.index_writer.memory.bytes", + "type": "alias" + }, + "memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.memory.bytes", + "type": "alias" + }, + "norms_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.norms.memory.bytes", + "type": "alias" + }, + "points_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.points.memory.bytes", + "type": "alias" + }, + "stored_fields_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.stored_fields.memory.bytes", + "type": "alias" + }, + "term_vectors_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.term_vectors.memory.bytes", + "type": "alias" + }, + "terms_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.terms.memory.bytes", + "type": "alias" + }, + "version_map_memory_in_bytes": { + "path": "elasticsearch.node.stats.indices.segments.version_map.memory.bytes", + "type": "alias" + } + } + }, + "store": { + "properties": { + "size": { + "properties": { + "bytes": { + "path": "elasticsearch.node.stats.indices.store.size.bytes", + "type": "alias" + } + } + }, + "size_in_bytes": { + "path": "elasticsearch.node.stats.indices.store.size.bytes", + "type": "alias" + } + } + } + } + }, + "jvm": { + "properties": { + "gc": { + "properties": { + "collectors": { + "properties": { + "old": { + "properties": { + "collection_count": { + "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.count", + "type": "alias" + }, + "collection_time_in_millis": { + "path": "elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms", + "type": "alias" + } + } + }, + "young": { + "properties": { + "collection_count": { + "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.count", + "type": "alias" + }, + "collection_time_in_millis": { + "path": "elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms", + "type": "alias" + } + } + } + } + } + } + }, + "mem": { + "properties": { + "heap_max_in_bytes": { + "path": "elasticsearch.node.stats.jvm.mem.heap.max.bytes", + "type": "alias" + }, + "heap_used_in_bytes": { + "path": "elasticsearch.node.stats.jvm.mem.heap.used.bytes", + "type": "alias" + }, + "heap_used_percent": { + "path": "elasticsearch.node.stats.jvm.mem.heap.used.pct", + "type": "alias" + } + } + } + } + }, + "node_id": { + "path": "elasticsearch.node.id", + "type": "alias" + }, + "os": { + "properties": { + "cgroup": { + "properties": { + "cpu": { + "properties": { + "cfs_quota_micros": { + "path": "elasticsearch.node.stats.os.cgroup.cpu.cfs.quota.us", + "type": "alias" + }, + "stat": { + "properties": { + "number_of_elapsed_periods": { + "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.elapsed_periods.count", + "type": "alias" + }, + "number_of_times_throttled": { + "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.times_throttled.count", + "type": "alias" + }, + "time_throttled_nanos": { + "path": "elasticsearch.node.stats.os.cgroup.cpu.stat.time_throttled.ns", + "type": "alias" + } + } + } + } + }, + "cpuacct": { + "properties": { + "usage_nanos": { + "path": "elasticsearch.node.stats.os.cgroup.cpuacct.usage.ns", + "type": "alias" + } + } + }, + "memory": { + "properties": { + "control_group": { + "path": "elasticsearch.node.stats.os.cgroup.memory.control_group", + "type": "alias" + }, + "limit_in_bytes": { + "path": "elasticsearch.node.stats.os.cgroup.memory.limit.bytes", + "type": "alias" + }, + "usage_in_bytes": { + "path": "elasticsearch.node.stats.os.cgroup.memory.usage.bytes", + "type": "alias" + } + } + } + } + }, + "cpu": { + "properties": { + "load_average": { + "properties": { + "1m": { + "path": "elasticsearch.node.stats.os.cpu.load_avg.1m", + "type": "alias" + } + } + } + } + } + } + }, + "process": { + "properties": { + "cpu": { + "properties": { + "percent": { + "path": "elasticsearch.node.stats.process.cpu.pct", + "type": "alias" + } + } + } + } + }, + "thread_pool": { + "properties": { + "bulk": { + "properties": { + "queue": { + "path": "elasticsearch.node.stats.thread_pool.bulk.queue.count", + "type": "alias" + }, + "rejected": { + "path": "elasticsearch.node.stats.thread_pool.bulk.rejected.count", + "type": "alias" + } + } + }, + "get": { + "properties": { + "queue": { + "path": "elasticsearch.node.stats.thread_pool.get.queue.count", + "type": "alias" + }, + "rejected": { + "path": "elasticsearch.node.stats.thread_pool.get.rejected.count", + "type": "alias" + } + } + }, + "index": { + "properties": { + "queue": { + "path": "elasticsearch.node.stats.thread_pool.index.queue.count", + "type": "alias" + }, + "rejected": { + "path": "elasticsearch.node.stats.thread_pool.index.rejected.count", + "type": "alias" + } + } + }, + "search": { + "properties": { + "queue": { + "path": "elasticsearch.node.stats.thread_pool.search.queue.count", + "type": "alias" + }, + "rejected": { + "path": "elasticsearch.node.stats.thread_pool.search.rejected.count", + "type": "alias" + } + } + }, + "write": { + "properties": { + "queue": { + "path": "elasticsearch.node.stats.thread_pool.write.queue.count", + "type": "alias" + }, + "rejected": { + "path": "elasticsearch.node.stats.thread_pool.write.rejected.count", + "type": "alias" + } + } + } + } + } + } + }, + "observer": { + "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zone": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "object" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "openmetrics": { + "properties": { + "exemplar": { + "properties": { + "*": { + "type": "object" + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + } + } + }, + "help": { + "ignore_above": 1024, + "type": "keyword" + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "metrics": { + "properties": { + "*": { + "type": "object" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "orchestrator": { + "properties": { + "api_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "cluster": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "resource": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "php_fpm": { + "properties": { + "pool": { + "properties": { + "connections": { + "properties": { + "accepted": { + "type": "long" + }, + "listen_queue_len": { + "type": "long" + }, + "max_listen_queue": { + "type": "long" + }, + "queued": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "process_manager": { + "ignore_above": 1024, + "type": "keyword" + }, + "processes": { + "properties": { + "active": { + "type": "long" + }, + "idle": { + "type": "long" + }, + "max_active": { + "type": "long" + }, + "max_children_reached": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "slow_requests": { + "type": "long" + }, + "start_since": { + "type": "long" + }, + "start_time": { + "type": "date" + } + } + }, + "process": { + "properties": { + "last_request_cpu": { + "type": "long" + }, + "last_request_memory": { + "type": "long" + }, + "request_duration": { + "type": "long" + }, + "requests": { + "type": "long" + }, + "script": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_since": { + "type": "long" + }, + "start_time": { + "type": "date" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "postgresql": { + "properties": { + "activity": { + "properties": { + "application_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "backend_start": { + "type": "date" + }, + "backend_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + } + } + }, + "database": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "oid": { + "type": "long" + } + } + }, + "pid": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "query_start": { + "type": "date" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_change": { + "type": "date" + }, + "transaction_start": { + "type": "date" + }, + "user": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "wait_event": { + "ignore_above": 1024, + "type": "keyword" + }, + "wait_event_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "waiting": { + "type": "boolean" + } + } + }, + "bgwriter": { + "properties": { + "buffers": { + "properties": { + "allocated": { + "type": "long" + }, + "backend": { + "type": "long" + }, + "backend_fsync": { + "type": "long" + }, + "checkpoints": { + "type": "long" + }, + "clean": { + "type": "long" + }, + "clean_full": { + "type": "long" + } + } + }, + "checkpoints": { + "properties": { + "requested": { + "type": "long" + }, + "scheduled": { + "type": "long" + }, + "times": { + "properties": { + "sync": { + "properties": { + "ms": { + "type": "float" + } + } + }, + "write": { + "properties": { + "ms": { + "type": "float" + } + } + } + } + } + } + }, + "stats_reset": { + "type": "date" + } + } + }, + "database": { + "properties": { + "blocks": { + "properties": { + "hit": { + "type": "long" + }, + "read": { + "type": "long" + }, + "time": { + "properties": { + "read": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "write": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "conflicts": { + "type": "long" + }, + "deadlocks": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "number_of_backends": { + "type": "long" + }, + "oid": { + "type": "long" + }, + "rows": { + "properties": { + "deleted": { + "type": "long" + }, + "fetched": { + "type": "long" + }, + "inserted": { + "type": "long" + }, + "returned": { + "type": "long" + }, + "updated": { + "type": "long" + } + } + }, + "stats_reset": { + "type": "date" + }, + "temporary": { + "properties": { + "bytes": { + "type": "long" + }, + "files": { + "type": "long" + } + } + }, + "transactions": { + "properties": { + "commit": { + "type": "long" + }, + "rollback": { + "type": "long" + } + } + } + } + }, + "statement": { + "properties": { + "database": { + "properties": { + "oid": { + "type": "long" + } + } + }, + "query": { + "properties": { + "calls": { + "type": "long" + }, + "id": { + "type": "long" + }, + "memory": { + "properties": { + "local": { + "properties": { + "dirtied": { + "type": "long" + }, + "hit": { + "type": "long" + }, + "read": { + "type": "long" + }, + "written": { + "type": "long" + } + } + }, + "shared": { + "properties": { + "dirtied": { + "type": "long" + }, + "hit": { + "type": "long" + }, + "read": { + "type": "long" + }, + "written": { + "type": "long" + } + } + }, + "temp": { + "properties": { + "read": { + "type": "long" + }, + "written": { + "type": "long" + } + } + } + } + }, + "rows": { + "type": "long" + }, + "text": { + "ignore_above": 1024, + "type": "keyword" + }, + "time": { + "properties": { + "max": { + "properties": { + "ms": { + "type": "float" + } + } + }, + "mean": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "min": { + "properties": { + "ms": { + "type": "float" + } + } + }, + "stddev": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "total": { + "properties": { + "ms": { + "type": "float" + } + } + } + } + } + } + }, + "user": { + "properties": { + "id": { + "type": "long" + } + } + } + } + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "cpu": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "start_time": { + "type": "date" + } + } + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "end": { + "type": "date" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "end": { + "type": "date" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "prometheus": { + "properties": { + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "metrics": { + "properties": { + "*": { + "type": "object" + } + } + }, + "query": { + "properties": { + "*": { + "type": "object" + } + } + } + } + }, + "rabbitmq": { + "properties": { + "connection": { + "properties": { + "channel_max": { + "type": "long" + }, + "channels": { + "type": "long" + }, + "client_provided": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "frame_max": { + "type": "long" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "octet_count": { + "properties": { + "received": { + "type": "long" + }, + "sent": { + "type": "long" + } + } + }, + "packet_count": { + "properties": { + "pending": { + "type": "long" + }, + "received": { + "type": "long" + }, + "sent": { + "type": "long" + } + } + }, + "peer": { + "properties": { + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + } + } + }, + "port": { + "type": "long" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "exchange": { + "properties": { + "auto_delete": { + "type": "boolean" + }, + "durable": { + "type": "boolean" + }, + "internal": { + "type": "boolean" + }, + "messages": { + "properties": { + "publish_in": { + "properties": { + "count": { + "type": "long" + }, + "details": { + "properties": { + "rate": { + "type": "float" + } + } + } + } + }, + "publish_out": { + "properties": { + "count": { + "type": "long" + }, + "details": { + "properties": { + "rate": { + "type": "float" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "node": { + "properties": { + "disk": { + "properties": { + "free": { + "properties": { + "bytes": { + "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "fd": { + "properties": { + "total": { + "type": "long" + }, + "used": { + "type": "long" + } + } + }, + "gc": { + "properties": { + "num": { + "properties": { + "count": { + "type": "long" + } + } + }, + "reclaimed": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "io": { + "properties": { + "file_handle": { + "properties": { + "open_attempt": { + "properties": { + "avg": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "count": { + "type": "long" + } + } + } + } + }, + "read": { + "properties": { + "avg": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "bytes": { + "type": "long" + }, + "count": { + "type": "long" + } + } + }, + "reopen": { + "properties": { + "count": { + "type": "long" + } + } + }, + "seek": { + "properties": { + "avg": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "count": { + "type": "long" + } + } + }, + "sync": { + "properties": { + "avg": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "count": { + "type": "long" + } + } + }, + "write": { + "properties": { + "avg": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "bytes": { + "type": "long" + }, + "count": { + "type": "long" + } + } + } + } + }, + "mem": { + "properties": { + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "mnesia": { + "properties": { + "disk": { + "properties": { + "tx": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "ram": { + "properties": { + "tx": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "msg": { + "properties": { + "store_read": { + "properties": { + "count": { + "type": "long" + } + } + }, + "store_write": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "proc": { + "properties": { + "total": { + "type": "long" + }, + "used": { + "type": "long" + } + } + }, + "processors": { + "type": "long" + }, + "queue": { + "properties": { + "index": { + "properties": { + "journal_write": { + "properties": { + "count": { + "type": "long" + } + } + }, + "read": { + "properties": { + "count": { + "type": "long" + } + } + }, + "write": { + "properties": { + "count": { + "type": "long" + } + } + } + } + } + } + }, + "run": { + "properties": { + "queue": { + "type": "long" + } + } + }, + "socket": { + "properties": { + "total": { + "type": "long" + }, + "used": { + "type": "long" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + } + } + }, + "queue": { + "properties": { + "arguments": { + "properties": { + "max_priority": { + "type": "long" + } + } + }, + "auto_delete": { + "type": "boolean" + }, + "consumers": { + "properties": { + "count": { + "type": "long" + }, + "utilisation": { + "properties": { + "pct": { + "type": "long" + } + } + } + } + }, + "disk": { + "properties": { + "reads": { + "properties": { + "count": { + "type": "long" + } + } + }, + "writes": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "durable": { + "type": "boolean" + }, + "exclusive": { + "type": "boolean" + }, + "memory": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "messages": { + "properties": { + "persistent": { + "properties": { + "count": { + "type": "long" + } + } + }, + "ready": { + "properties": { + "count": { + "type": "long" + }, + "details": { + "properties": { + "rate": { + "type": "float" + } + } + } + } + }, + "total": { + "properties": { + "count": { + "type": "long" + }, + "details": { + "properties": { + "rate": { + "type": "float" + } + } + } + } + }, + "unacknowledged": { + "properties": { + "count": { + "type": "long" + }, + "details": { + "properties": { + "rate": { + "type": "float" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shovel": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vhost": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "redis": { + "properties": { + "info": { + "properties": { + "clients": { + "properties": { + "blocked": { + "type": "long" + }, + "connected": { + "type": "long" + }, + "max_input_buffer": { + "type": "long" + }, + "max_output_buffer": { + "type": "long" + } + } + }, + "cluster": { + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "commandstats": { + "properties": { + "*": { + "properties": { + "calls": { + "type": "long" + }, + "failed_calls": { + "type": "long" + }, + "rejected_calls": { + "type": "long" + }, + "usec": { + "type": "long" + }, + "usec_per_call": { + "type": "float" + } + } + } + } + }, + "cpu": { + "properties": { + "used": { + "properties": { + "sys": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "sys_children": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "user": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "user_children": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "memory": { + "properties": { + "active_defrag": { + "properties": { + "is_running": { + "type": "boolean" + } + } + }, + "allocator": { + "ignore_above": 1024, + "type": "keyword" + }, + "allocator_stats": { + "properties": { + "active": { + "type": "long" + }, + "allocated": { + "type": "long" + }, + "fragmentation": { + "properties": { + "bytes": { + "type": "long" + }, + "ratio": { + "type": "float" + } + } + }, + "resident": { + "type": "long" + }, + "rss": { + "properties": { + "bytes": { + "type": "long" + }, + "ratio": { + "type": "float" + } + } + } + } + }, + "fragmentation": { + "properties": { + "bytes": { + "type": "long" + }, + "ratio": { + "type": "float" + } + } + }, + "max": { + "properties": { + "policy": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "type": "long" + } + } + }, + "used": { + "properties": { + "dataset": { + "type": "long" + }, + "lua": { + "type": "long" + }, + "peak": { + "type": "long" + }, + "rss": { + "type": "long" + }, + "value": { + "type": "long" + } + } + } + } + }, + "persistence": { + "properties": { + "aof": { + "properties": { + "bgrewrite": { + "properties": { + "last_status": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "buffer": { + "properties": { + "size": { + "type": "long" + } + } + }, + "copy_on_write": { + "properties": { + "last_size": { + "type": "long" + } + } + }, + "enabled": { + "type": "boolean" + }, + "fsync": { + "properties": { + "delayed": { + "type": "long" + }, + "pending": { + "type": "long" + } + } + }, + "rewrite": { + "properties": { + "buffer": { + "properties": { + "size": { + "type": "long" + } + } + }, + "current_time": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "in_progress": { + "type": "boolean" + }, + "last_time": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "scheduled": { + "type": "boolean" + } + } + }, + "size": { + "properties": { + "base": { + "type": "long" + }, + "current": { + "type": "long" + } + } + }, + "write": { + "properties": { + "last_status": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "loading": { + "type": "boolean" + }, + "rdb": { + "properties": { + "bgsave": { + "properties": { + "current_time": { + "properties": { + "sec": { + "type": "long" + } + } + }, + "in_progress": { + "type": "boolean" + }, + "last_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "last_time": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + }, + "copy_on_write": { + "properties": { + "last_size": { + "type": "long" + } + } + }, + "last_save": { + "properties": { + "changes_since": { + "type": "long" + }, + "time": { + "type": "long" + } + } + } + } + } + } + }, + "replication": { + "properties": { + "backlog": { + "properties": { + "active": { + "type": "long" + }, + "first_byte_offset": { + "type": "long" + }, + "histlen": { + "type": "long" + }, + "size": { + "type": "long" + } + } + }, + "connected_slaves": { + "type": "long" + }, + "master": { + "properties": { + "last_io_seconds_ago": { + "type": "long" + }, + "link_status": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "type": "long" + }, + "second_offset": { + "type": "long" + }, + "sync": { + "properties": { + "in_progress": { + "type": "boolean" + }, + "last_io_seconds_ago": { + "type": "long" + }, + "left_bytes": { + "type": "long" + } + } + } + } + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "slave": { + "properties": { + "is_readonly": { + "type": "boolean" + }, + "offset": { + "type": "long" + }, + "priority": { + "type": "long" + } + } + } + } + }, + "server": { + "properties": { + "arch_bits": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "config_file": { + "ignore_above": 1024, + "type": "keyword" + }, + "gcc_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "git_dirty": { + "ignore_above": 1024, + "type": "keyword" + }, + "git_sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "hz": { + "type": "long" + }, + "lru_clock": { + "type": "long" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "multiplexing_api": { + "ignore_above": 1024, + "type": "keyword" + }, + "run_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "tcp_port": { + "type": "long" + }, + "uptime": { + "type": "long" + } + } + }, + "slowlog": { + "properties": { + "count": { + "type": "long" + } + } + }, + "stats": { + "properties": { + "active_defrag": { + "properties": { + "hits": { + "type": "long" + }, + "key_hits": { + "type": "long" + }, + "key_misses": { + "type": "long" + }, + "misses": { + "type": "long" + } + } + }, + "commands_processed": { + "type": "long" + }, + "connections": { + "properties": { + "received": { + "type": "long" + }, + "rejected": { + "type": "long" + } + } + }, + "instantaneous": { + "properties": { + "input_kbps": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ops_per_sec": { + "type": "long" + }, + "output_kbps": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "keys": { + "properties": { + "evicted": { + "type": "long" + }, + "expired": { + "type": "long" + } + } + }, + "keyspace": { + "properties": { + "hits": { + "type": "long" + }, + "misses": { + "type": "long" + } + } + }, + "latest_fork_usec": { + "type": "long" + }, + "migrate_cached_sockets": { + "type": "long" + }, + "net": { + "properties": { + "input": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "output": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "pubsub": { + "properties": { + "channels": { + "type": "long" + }, + "patterns": { + "type": "long" + } + } + }, + "slave_expires_tracked_keys": { + "type": "long" + }, + "sync": { + "properties": { + "full": { + "type": "long" + }, + "partial": { + "properties": { + "err": { + "type": "long" + }, + "ok": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "key": { + "properties": { + "expire": { + "properties": { + "ttl": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "length": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "keyspace": { + "properties": { + "avg_ttl": { + "type": "long" + }, + "expires": { + "type": "long" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "keys": { + "type": "long" + } + } + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "hosts": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "author": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "origin": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "environment": { + "ignore_above": 1024, + "type": "keyword" + }, + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shard": { + "properties": { + "index": { + "path": "elasticsearch.index.name", + "type": "alias" + }, + "node": { + "path": "elasticsearch.node.id", + "type": "alias" + }, + "primary": { + "path": "elasticsearch.shard.primary", + "type": "alias" + }, + "shard": { + "path": "elasticsearch.shard.number", + "type": "alias" + }, + "state": { + "path": "elasticsearch.shard.state", + "type": "alias" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "source_node": { + "properties": { + "name": { + "path": "elasticsearch.node.name", + "type": "alias" + }, + "uuid": { + "path": "elasticsearch.node.id", + "type": "alias" + } + } + }, + "span": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "stack_stats": { + "properties": { + "apm": { + "properties": { + "found": { + "path": "elasticsearch.cluster.stats.stack.apm.found", + "type": "alias" + } + } + }, + "xpack": { + "properties": { + "ccr": { + "properties": { + "available": { + "path": "elasticsearch.cluster.stats.stack.xpack.ccr.available", + "type": "alias" + }, + "enabled": { + "path": "elasticsearch.cluster.stats.stack.xpack.ccr.enabled", + "type": "alias" + } + } + } + } + } + } + }, + "system": { + "properties": { + "core": { + "properties": { + "core_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "type": "long" + }, + "idle": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "iowait": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "irq": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "mhz": { + "type": "float" + }, + "model_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "model_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "nice": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "physical_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "softirq": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "steal": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "system": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "total": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "user": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + } + } + }, + "cpu": { + "properties": { + "cores": { + "type": "long" + }, + "idle": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "iowait": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "irq": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "nice": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "softirq": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "steal": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "system": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + }, + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "user": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + } + } + } + } + }, + "diskio": { + "properties": { + "io": { + "properties": { + "ops": { + "type": "long" + }, + "time": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "read": { + "properties": { + "bytes": { + "type": "long" + }, + "count": { + "type": "long" + }, + "time": { + "type": "long" + } + } + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "write": { + "properties": { + "bytes": { + "type": "long" + }, + "count": { + "type": "long" + }, + "time": { + "type": "long" + } + } + } + } + }, + "entropy": { + "properties": { + "available_bits": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "filesystem": { + "properties": { + "available": { + "type": "long" + }, + "device_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "files": { + "type": "long" + }, + "free": { + "type": "long" + }, + "free_files": { + "type": "long" + }, + "mount_point": { + "ignore_above": 1024, + "type": "keyword" + }, + "options": { + "ignore_above": 1024, + "type": "keyword" + }, + "total": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "fsstat": { + "properties": { + "count": { + "type": "long" + }, + "total_files": { + "type": "long" + }, + "total_size": { + "properties": { + "free": { + "type": "long" + }, + "total": { + "type": "long" + }, + "used": { + "type": "long" + } + } + } + } + }, + "load": { + "properties": { + "1": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "15": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "5": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "cores": { + "type": "long" + }, + "norm": { + "properties": { + "1": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "15": { + "scaling_factor": 100, + "type": "scaled_float" + }, + "5": { + "scaling_factor": 100, + "type": "scaled_float" + } + } + } + } + }, + "memory": { + "properties": { + "actual": { + "properties": { + "free": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "cached": { + "type": "long" + }, + "free": { + "type": "long" + }, + "swap": { + "properties": { + "free": { + "type": "long" + }, + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "total": { + "type": "long" + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "network": { + "properties": { + "in": { + "properties": { + "bytes": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "out": { + "properties": { + "bytes": { + "type": "long" + }, + "dropped": { + "type": "long" + }, + "errors": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "network_summary": { + "properties": { + "icmp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "ip": { + "properties": { + "*": { + "type": "object" + } + } + }, + "tcp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "udp": { + "properties": { + "*": { + "type": "object" + } + } + }, + "udp_lite": { + "properties": { + "*": { + "type": "object" + } + } + } + } + }, + "process": { + "properties": { + "cgroup": { + "properties": { + "blkio": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "total": { + "properties": { + "bytes": { + "type": "long" + }, + "ios": { + "type": "long" + } + } + } + } + }, + "cgroups_version": { + "type": "long" + }, + "cpu": { + "properties": { + "cfs": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } + } + }, + "quota": { + "properties": { + "us": { + "type": "long" + } + } + }, + "shares": { + "type": "long" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pressure": { + "properties": { + "full": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "type": "long" + } + } + }, + "some": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "type": "long" + } + } + } + } + }, + "rt": { + "properties": { + "period": { + "properties": { + "us": { + "type": "long" + } + } + }, + "runtime": { + "properties": { + "us": { + "type": "long" + } + } + } + } + }, + "stats": { + "properties": { + "periods": { + "type": "long" + }, + "system": { + "properties": { + "norm": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "ns": { + "type": "long" + }, + "pct": { + "type": "float" + } + } + }, + "throttled": { + "properties": { + "ns": { + "type": "long" + }, + "periods": { + "type": "long" + }, + "us": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "norm": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "ns": { + "type": "long" + }, + "pct": { + "type": "float" + } + } + }, + "user": { + "properties": { + "norm": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "ns": { + "type": "long" + }, + "pct": { + "type": "float" + } + } + } + } + } + } + }, + "cpuacct": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "percpu": { + "type": "object" + }, + "stats": { + "properties": { + "system": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "ns": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "user": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "ns": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "ns": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "io": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "pressure": { + "properties": { + "full": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "type": "long" + } + } + }, + "some": { + "properties": { + "10": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "300": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "60": { + "properties": { + "pct": { + "type": "float" + } + } + }, + "total": { + "type": "long" + } + } + } + } + }, + "stats": { + "properties": { + "*": { + "properties": { + "*": { + "properties": { + "bytes": { + "type": "object" + }, + "ios": { + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + } + } + } + } + }, + "memory": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "kmem": { + "properties": { + "failures": { + "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "kmem_tcp": { + "properties": { + "failures": { + "type": "long" + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "mem": { + "properties": { + "events": { + "properties": { + "fail": { + "type": "long" + }, + "high": { + "type": "long" + }, + "low": { + "type": "long" + }, + "max": { + "type": "long" + }, + "oom": { + "type": "long" + }, + "oom_kill": { + "type": "long" + } + } + }, + "failures": { + "type": "long" + }, + "high": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "low": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "memsw": { + "properties": { + "events": { + "properties": { + "fail": { + "type": "long" + }, + "high": { + "type": "long" + }, + "low": { + "type": "long" + }, + "max": { + "type": "long" + }, + "oom": { + "type": "long" + }, + "oom_kill": { + "type": "long" + } + } + }, + "failures": { + "type": "long" + }, + "high": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "low": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "usage": { + "properties": { + "bytes": { + "type": "long" + }, + "max": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "stats": { + "properties": { + "*": { + "properties": { + "bytes": { + "type": "object" + } + }, + "type": "object" + }, + "active_anon": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "active_file": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "cache": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "hierarchical_memory_limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "hierarchical_memsw_limit": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "inactive_anon": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "inactive_file": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "major_page_faults": { + "type": "long" + }, + "mapped_file": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "page_faults": { + "type": "long" + }, + "pages_in": { + "type": "long" + }, + "pages_out": { + "type": "long" + }, + "rss": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "rss_huge": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "swap": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "unevictable": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "cmdline": { + "ignore_above": 2048, + "type": "keyword" + }, + "cpu": { + "properties": { + "start_time": { + "type": "date" + }, + "system": { + "properties": { + "ticks": { + "type": "long" + } + } + }, + "total": { + "properties": { + "norm": { + "properties": { + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + }, + "ticks": { + "type": "long" + }, + "value": { + "type": "long" + } + } + }, + "user": { + "properties": { + "ticks": { + "type": "long" + } + } + } + } + }, + "env": { + "type": "object" + }, + "fd": { + "properties": { + "limit": { + "properties": { + "hard": { + "type": "long" + }, + "soft": { + "type": "long" + } + } + }, + "open": { + "type": "long" + } + } + }, + "memory": { + "properties": { + "rss": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + }, + "share": { + "type": "long" + }, + "size": { + "type": "long" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "dead": { + "type": "long" + }, + "idle": { + "type": "long" + }, + "parked": { + "type": "long" + }, + "running": { + "type": "long" + }, + "sleeping": { + "type": "long" + }, + "stopped": { + "type": "long" + }, + "total": { + "type": "long" + }, + "unknown": { + "type": "long" + }, + "wake": { + "type": "long" + }, + "wakekill": { + "type": "long" + }, + "zombie": { + "type": "long" + } + } + } + } + }, + "raid": { + "properties": { + "blocks": { + "properties": { + "synced": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "disks": { + "properties": { + "active": { + "type": "long" + }, + "failed": { + "type": "long" + }, + "spare": { + "type": "long" + }, + "states": { + "properties": { + "*": { + "type": "object" + } + } + }, + "total": { + "type": "long" + } + } + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "sync_action": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "service": { + "properties": { + "exec_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "load_state": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "resources": { + "properties": { + "cpu": { + "properties": { + "usage": { + "properties": { + "ns": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "usage": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "network": { + "properties": { + "in": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + }, + "out": { + "properties": { + "bytes": { + "type": "long" + }, + "packets": { + "type": "long" + } + } + } + } + }, + "tasks": { + "properties": { + "count": { + "type": "long" + } + } + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_since": { + "type": "date" + }, + "sub_state": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit_file": { + "properties": { + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor_preset": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "socket": { + "properties": { + "local": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "process": { + "properties": { + "cmdline": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "remote": { + "properties": { + "etld_plus_one": { + "ignore_above": 1024, + "type": "keyword" + }, + "host": { + "ignore_above": 1024, + "type": "keyword" + }, + "host_error": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "summary": { + "properties": { + "all": { + "properties": { + "count": { + "type": "long" + }, + "listening": { + "type": "long" + } + } + }, + "tcp": { + "properties": { + "all": { + "properties": { + "close_wait": { + "type": "long" + }, + "closing": { + "type": "long" + }, + "count": { + "type": "long" + }, + "established": { + "type": "long" + }, + "fin_wait1": { + "type": "long" + }, + "fin_wait2": { + "type": "long" + }, + "last_ack": { + "type": "long" + }, + "listening": { + "type": "long" + }, + "orphan": { + "type": "long" + }, + "syn_recv": { + "type": "long" + }, + "syn_sent": { + "type": "long" + }, + "time_wait": { + "type": "long" + } + } + }, + "memory": { + "type": "long" + } + } + }, + "udp": { + "properties": { + "all": { + "properties": { + "count": { + "type": "long" + } + } + }, + "memory": { + "type": "long" + } + } + } + } + } + } + }, + "uptime": { + "properties": { + "duration": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "users": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "leader": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "remote": { + "type": "boolean" + }, + "remote_host": { + "ignore_above": 1024, + "type": "keyword" + }, + "scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "seat": { + "ignore_above": 1024, + "type": "keyword" + }, + "service": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "systemd": { + "properties": { + "fragment_path": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "enrichments": { + "properties": { + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "confidence": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "modified_at": { + "type": "date" + }, + "port": { + "type": "long" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "object" + }, + "matched": { + "properties": { + "atomic": { + "ignore_above": 1024, + "type": "keyword" + }, + "field": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "index": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "indicator": { + "properties": { + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "confidence": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "code_signature": { + "properties": { + "digest_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "exists": { + "type": "boolean" + }, + "signing_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "team_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "elf": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "byte_order": { + "ignore_above": 1024, + "type": "keyword" + }, + "cpu_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "creation_date": { + "type": "date" + }, + "exports": { + "type": "flattened" + }, + "header": { + "properties": { + "abi_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "entrypoint": { + "type": "long" + }, + "object_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "os_abi": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "imports": { + "type": "flattened" + }, + "sections": { + "properties": { + "chi2": { + "type": "long" + }, + "entropy": { + "type": "long" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_offset": { + "ignore_above": 1024, + "type": "keyword" + }, + "physical_size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "virtual_address": { + "type": "long" + }, + "virtual_size": { + "type": "long" + } + }, + "type": "nested" + }, + "segments": { + "properties": { + "sections": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "type": "nested" + }, + "shared_libraries": { + "ignore_above": 1024, + "type": "keyword" + }, + "telfhash": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fork_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "ssdeep": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mime_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pe": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "company": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "file_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "imphash": { + "ignore_above": 1024, + "type": "keyword" + }, + "original_file_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "first_seen": { + "type": "date" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "postal_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "last_seen": { + "type": "date" + }, + "marking": { + "properties": { + "tlp": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "modified_at": { + "type": "date" + }, + "port": { + "type": "long" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "scanner_stats": { + "type": "long" + }, + "sightings": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "software": { + "properties": { + "alias": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platforms": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "subtechnique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timestamp": { + "path": "@timestamp", + "type": "alias" + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "traefik": { + "properties": { + "health": { + "properties": { + "response": { + "properties": { + "avg_time": { + "properties": { + "us": { + "type": "long" + } + } + }, + "count": { + "type": "long" + }, + "status_codes": { + "properties": { + "*": { + "type": "object" + } + } + } + } + }, + "uptime": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "changes": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + }, + "target": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "roles": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "uwsgi": { + "properties": { + "status": { + "properties": { + "core": { + "properties": { + "id": { + "type": "long" + }, + "read_errors": { + "type": "long" + }, + "requests": { + "properties": { + "offloaded": { + "type": "long" + }, + "routed": { + "type": "long" + }, + "static": { + "type": "long" + }, + "total": { + "type": "long" + } + } + }, + "worker_pid": { + "type": "long" + }, + "write_errors": { + "type": "long" + } + } + }, + "total": { + "properties": { + "exceptions": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "read_errors": { + "type": "long" + }, + "requests": { + "type": "long" + }, + "write_errors": { + "type": "long" + } + } + }, + "worker": { + "properties": { + "accepting": { + "type": "long" + }, + "avg_rt": { + "type": "long" + }, + "delta_requests": { + "type": "long" + }, + "exceptions": { + "type": "long" + }, + "harakiri_count": { + "type": "long" + }, + "id": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "requests": { + "type": "long" + }, + "respawn_count": { + "type": "long" + }, + "rss": { + "type": "long" + }, + "running_time": { + "type": "long" + }, + "signal_queue": { + "type": "long" + }, + "signals": { + "type": "long" + }, + "status": { + "ignore_above": 1024, + "type": "keyword" + }, + "tx": { + "type": "long" + }, + "vsz": { + "type": "long" + } + } + } + } + } + } + }, + "vlan": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vsphere": { + "properties": { + "datastore": { + "properties": { + "capacity": { + "properties": { + "free": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + }, + "pct": { + "scaling_factor": 1000, + "type": "scaled_float" + } + } + } + } + }, + "fstype": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "cpu": { + "properties": { + "free": { + "properties": { + "mhz": { + "type": "long" + } + } + }, + "total": { + "properties": { + "mhz": { + "type": "long" + } + } + }, + "used": { + "properties": { + "mhz": { + "type": "long" + } + } + } + } + }, + "memory": { + "properties": { + "free": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "total": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "used": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network_names": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "virtualmachine": { + "properties": { + "cpu": { + "properties": { + "free": { + "properties": { + "mhz": { + "type": "long" + } + } + }, + "total": { + "properties": { + "mhz": { + "type": "long" + } + } + }, + "used": { + "properties": { + "mhz": { + "type": "long" + } + } + } + } + }, + "custom_fields": { + "type": "object" + }, + "host": { + "properties": { + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "memory": { + "properties": { + "free": { + "properties": { + "guest": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "total": { + "properties": { + "guest": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + }, + "used": { + "properties": { + "guest": { + "properties": { + "bytes": { + "type": "long" + } + } + }, + "host": { + "properties": { + "bytes": { + "type": "long" + } + } + } + } + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "network_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "windows": { + "properties": { + "perfmon": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + }, + "metrics": { + "properties": { + "*": { + "properties": { + "*": { + "type": "object" + } + } + } + } + } + } + }, + "service": { + "properties": { + "display_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "pid": { + "type": "long" + }, + "start_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "start_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "x509": { + "properties": { + "alternative_names": { + "ignore_above": 1024, + "type": "keyword" + }, + "issuer": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "public_key_exponent": { + "doc_values": false, + "index": false, + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "signature_algorithm": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "properties": { + "common_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country": { + "ignore_above": 1024, + "type": "keyword" + }, + "distinguished_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "locality": { + "ignore_above": 1024, + "type": "keyword" + }, + "organization": { + "ignore_above": 1024, + "type": "keyword" + }, + "organizational_unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "state_or_province": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version_number": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "zookeeper": { + "properties": { + "connection": { + "properties": { + "interest_ops": { + "type": "long" + }, + "queued": { + "type": "long" + }, + "received": { + "type": "long" + }, + "sent": { + "type": "long" + } + } + }, + "mntr": { + "properties": { + "approximate_data_size": { + "type": "long" + }, + "ephemerals_count": { + "type": "long" + }, + "followers": { + "type": "long" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "latency": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "learners": { + "type": "long" + }, + "max_file_descriptor_count": { + "type": "long" + }, + "num_alive_connections": { + "type": "long" + }, + "open_file_descriptor_count": { + "type": "long" + }, + "outstanding_requests": { + "type": "long" + }, + "packets": { + "properties": { + "received": { + "type": "long" + }, + "sent": { + "type": "long" + } + } + }, + "pending_syncs": { + "type": "long" + }, + "server_state": { + "ignore_above": 1024, + "type": "keyword" + }, + "synced_followers": { + "type": "long" + }, + "version": { + "path": "service.version", + "type": "alias" + }, + "watch_count": { + "type": "long" + }, + "znode_count": { + "type": "long" + } + } + }, + "server": { + "properties": { + "connections": { + "type": "long" + }, + "count": { + "type": "long" + }, + "epoch": { + "type": "long" + }, + "latency": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "node_count": { + "type": "long" + }, + "outstanding": { + "type": "long" + }, + "received": { + "type": "long" + }, + "sent": { + "type": "long" + }, + "version_date": { + "type": "date" + }, + "zxid": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "settings": { + "index": { + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "max_docvalue_fields_search": "200", + "query": { + "default_field": [ + "message", + "tags", + "agent.ephemeral_id", + "agent.id", + "agent.name", + "agent.type", + "agent.version", + "as.organization.name", + "client.address", + "client.as.organization.name", + "client.domain", + "client.geo.city_name", + "client.geo.continent_name", + "client.geo.country_iso_code", + "client.geo.country_name", + "client.geo.name", + "client.geo.region_iso_code", + "client.geo.region_name", + "client.mac", + "client.registered_domain", + "client.top_level_domain", + "client.user.domain", + "client.user.email", + "client.user.full_name", + "client.user.group.domain", + "client.user.group.id", + "client.user.group.name", + "client.user.hash", + "client.user.id", + "client.user.name", + "cloud.account.id", + "cloud.availability_zone", + "cloud.instance.id", + "cloud.instance.name", + "cloud.machine.type", + "cloud.provider", + "cloud.region", + "container.id", + "container.image.name", + "container.image.tag", + "container.name", + "container.runtime", + "destination.address", + "destination.as.organization.name", + "destination.domain", + "destination.geo.city_name", + "destination.geo.continent_name", + "destination.geo.country_iso_code", + "destination.geo.country_name", + "destination.geo.name", + "destination.geo.region_iso_code", + "destination.geo.region_name", + "destination.mac", + "destination.registered_domain", + "destination.top_level_domain", + "destination.user.domain", + "destination.user.email", + "destination.user.full_name", + "destination.user.group.domain", + "destination.user.group.id", + "destination.user.group.name", + "destination.user.hash", + "destination.user.id", + "destination.user.name", + "dns.answers.class", + "dns.answers.data", + "dns.answers.name", + "dns.answers.type", + "dns.header_flags", + "dns.id", + "dns.op_code", + "dns.question.class", + "dns.question.name", + "dns.question.registered_domain", + "dns.question.subdomain", + "dns.question.top_level_domain", + "dns.question.type", + "dns.response_code", + "dns.type", + "ecs.version", + "error.code", + "error.id", + "error.message", + "error.stack_trace", + "error.type", + "event.action", + "event.category", + "event.code", + "event.dataset", + "event.hash", + "event.id", + "event.kind", + "event.module", + "event.outcome", + "event.provider", + "event.timezone", + "event.type", + "file.device", + "file.directory", + "file.extension", + "file.gid", + "file.group", + "file.hash.md5", + "file.hash.sha1", + "file.hash.sha256", + "file.hash.sha512", + "file.inode", + "file.mode", + "file.name", + "file.owner", + "file.path", + "file.target_path", + "file.type", + "file.uid", + "geo.city_name", + "geo.continent_name", + "geo.country_iso_code", + "geo.country_name", + "geo.name", + "geo.region_iso_code", + "geo.region_name", + "group.domain", + "group.id", + "group.name", + "hash.md5", + "hash.sha1", + "hash.sha256", + "hash.sha512", + "host.architecture", + "host.geo.city_name", + "host.geo.continent_name", + "host.geo.country_iso_code", + "host.geo.country_name", + "host.geo.name", + "host.geo.region_iso_code", + "host.geo.region_name", + "host.hostname", + "host.id", + "host.mac", + "host.name", + "host.os.family", + "host.os.full", + "host.os.kernel", + "host.os.name", + "host.os.platform", + "host.os.version", + "host.type", + "http.request.body.content", + "http.request.method", + "http.request.referrer", + "http.response.body.content", + "http.version", + "log.level", + "log.logger", + "log.origin.file.name", + "log.origin.function", + "log.syslog.facility.name", + "log.syslog.severity.name", + "network.application", + "network.community_id", + "network.direction", + "network.iana_number", + "network.name", + "network.protocol", + "network.transport", + "network.type", + "observer.geo.city_name", + "observer.geo.continent_name", + "observer.geo.country_iso_code", + "observer.geo.country_name", + "observer.geo.name", + "observer.geo.region_iso_code", + "observer.geo.region_name", + "observer.hostname", + "observer.mac", + "observer.name", + "observer.os.family", + "observer.os.full", + "observer.os.kernel", + "observer.os.name", + "observer.os.platform", + "observer.os.version", + "observer.product", + "observer.serial_number", + "observer.type", + "observer.vendor", + "observer.version", + "organization.id", + "organization.name", + "os.family", + "os.full", + "os.kernel", + "os.name", + "os.platform", + "os.version", + "package.architecture", + "package.checksum", + "package.description", + "package.install_scope", + "package.license", + "package.name", + "package.path", + "package.version", + "process.args", + "process.executable", + "process.hash.md5", + "process.hash.sha1", + "process.hash.sha256", + "process.hash.sha512", + "process.name", + "process.thread.name", + "process.title", + "process.working_directory", + "server.address", + "server.as.organization.name", + "server.domain", + "server.geo.city_name", + "server.geo.continent_name", + "server.geo.country_iso_code", + "server.geo.country_name", + "server.geo.name", + "server.geo.region_iso_code", + "server.geo.region_name", + "server.mac", + "server.registered_domain", + "server.top_level_domain", + "server.user.domain", + "server.user.email", + "server.user.full_name", + "server.user.group.domain", + "server.user.group.id", + "server.user.group.name", + "server.user.hash", + "server.user.id", + "server.user.name", + "service.ephemeral_id", + "service.id", + "service.name", + "service.node.name", + "service.state", + "service.type", + "service.version", + "source.address", + "source.as.organization.name", + "source.domain", + "source.geo.city_name", + "source.geo.continent_name", + "source.geo.country_iso_code", + "source.geo.country_name", + "source.geo.name", + "source.geo.region_iso_code", + "source.geo.region_name", + "source.mac", + "source.registered_domain", + "source.top_level_domain", + "source.user.domain", + "source.user.email", + "source.user.full_name", + "source.user.group.domain", + "source.user.group.id", + "source.user.group.name", + "source.user.hash", + "source.user.id", + "source.user.name", + "threat.framework", + "threat.tactic.id", + "threat.tactic.name", + "threat.tactic.reference", + "threat.technique.id", + "threat.technique.name", + "threat.technique.reference", + "trace.id", + "transaction.id", + "url.domain", + "url.extension", + "url.fragment", + "url.full", + "url.original", + "url.password", + "url.path", + "url.query", + "url.registered_domain", + "url.scheme", + "url.top_level_domain", + "url.username", + "user.domain", + "user.email", + "user.full_name", + "user.group.domain", + "user.group.id", + "user.group.name", + "user.hash", + "user.id", + "user.name", + "user_agent.device.name", + "user_agent.name", + "user_agent.original.text", + "user_agent.original", + "user_agent.os.family", + "user_agent.os.full", + "user_agent.os.kernel", + "user_agent.os.name", + "user_agent.os.platform", + "user_agent.os.version", + "user_agent.version", + "cloud.image.id", + "host.os.build", + "host.os.codename", + "kubernetes.pod.name", + "kubernetes.pod.uid", + "kubernetes.namespace", + "kubernetes.node.name", + "kubernetes.node.hostname", + "kubernetes.replicaset.name", + "kubernetes.deployment.name", + "kubernetes.statefulset.name", + "kubernetes.container.name", + "process.owner.id", + "process.owner.name.text", + "process.owner.name", + "jolokia.agent.version", + "jolokia.agent.id", + "jolokia.server.product", + "jolokia.server.version", + "jolokia.server.vendor", + "jolokia.url", + "fields.*" + ] + }, + "refresh_interval": "5s" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/functional/services/cases/create.ts b/x-pack/test/functional/services/cases/create.ts index f1aec2c8a51be..706be46187362 100644 --- a/x-pack/test/functional/services/cases/create.ts +++ b/x-pack/test/functional/services/cases/create.ts @@ -130,7 +130,7 @@ export function CasesCreateViewServiceProvider( async createCaseFromModal(params: CreateCaseParams) { await casesCommon.assertCaseModalVisible(true); - await retry.tryForTime(5000, async () => { + await retry.tryForTime(10000, async () => { await testSubjects.click('cases-table-add-case-filter-bar'); await casesCommon.assertCaseModalVisible(false); }); diff --git a/x-pack/test/functional_enterprise_search/README.md b/x-pack/test/functional_enterprise_search/README.md deleted file mode 100644 index c1be4e06c1eb9..0000000000000 --- a/x-pack/test/functional_enterprise_search/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Enterprise Search Functional E2E Tests - -## Running these tests - -Follow the [Functional Test Runner instructions](https://www.elastic.co/guide/en/kibana/current/development-tests.html#development-functional-tests#_running_functional_tests). - -There are two suites available to run, a suite that requires a Kibana instance without an `enterpriseSearch.host` -configured, and one that does. The later also [requires a running Enterprise Search instance](#enterprise-search-requirement), and a Private API key -from that instance set in an Environment variable. - -Ex. - -```sh -# Run specs from the x-pack directory -cd x-pack - -# Run tests that do not require enterpriseSearch.host variable -node scripts/functional_tests --config test/functional_enterprise_search/without_host_configured.config.ts - -# Run tests that require enterpriseSearch.host variable -APP_SEARCH_API_KEY=[use private key from local App Search instance here] node scripts/functional_tests --config test/functional_enterprise_search/with_host_configured.config.ts -``` - -## Enterprise Search Requirement - -The `with_host_configured` tests will not currently start an instance of App Search automatically. As such, they are not run as part of CI and are most useful for local regression testing. - -The easiest way to start Enterprise Search for these tests is to check out the `ent-search` project -and use the following script. - -```sh -cd script/stack_scripts -/start-with-license-and-expiration.sh platinum 500000 -``` - -Requirements for Enterprise Search: - -- Running on port 3002 against a separate Elasticsearch cluster. -- Elasticsearch must have a platinum or greater level license (or trial). -- Must have Standard or Native Auth configured with an `enterprise_search` user with password `changeme`. -- There should be NO existing Engines or Meta Engines. diff --git a/x-pack/test/functional_enterprise_search/apps/enterprise_search/with_host_configured/app_search/engines.ts b/x-pack/test/functional_enterprise_search/apps/enterprise_search/with_host_configured/app_search/engines.ts deleted file mode 100644 index b6a84687edd15..0000000000000 --- a/x-pack/test/functional_enterprise_search/apps/enterprise_search/with_host_configured/app_search/engines.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import type { Browser } from '@kbn/ftr-common-functional-ui-services'; -import { AppSearchService, IEngine } from '../../../../services/app_search_service'; -import { FtrProviderContext } from '../../../../ftr_provider_context'; - -export default function enterpriseSearchSetupEnginesTests({ - getService, - getPageObjects, -}: FtrProviderContext) { - const browser = getService('browser') as Browser; - const retry = getService('retry'); - const appSearch = getService('appSearch') as AppSearchService; - const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['appSearch', 'security']); - - describe('Engines Overview', function () { - let engine1: IEngine; - let engine2: IEngine; - let metaEngine: IEngine; - - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - engine1 = await appSearch.createEngine(); - engine2 = await appSearch.createEngine(); - metaEngine = await appSearch.createMetaEngine([engine1.name, engine2.name]); - }); - - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - await appSearch.destroyEngine(engine1.name); - await appSearch.destroyEngine(engine2.name); - await appSearch.destroyEngine(metaEngine.name); - }); - - describe('when an enterpriseSearch.host is configured', () => { - it('navigating to the enterprise_search plugin will redirect a user to the App Search Engines Overview page', async () => { - await PageObjects.security.forceLogout(); - const { user, password } = appSearch.getEnterpriseSearchUser(); - await PageObjects.security.login(user, password, { - expectSpaceSelector: false, - }); - - await PageObjects.appSearch.navigateToPage(); - await retry.try(async function () { - const currentUrl = await browser.getCurrentUrl(); - expect(currentUrl).to.contain('/app_search'); - - const documentTitle = await browser.getTitle(); - expect(documentTitle).to.contain('App Search - Elastic'); - }); - }); - - it('lists engines', async () => { - const engineLinks = await PageObjects.appSearch.getEngineLinks(); - const engineLinksText = await Promise.all(engineLinks.map((l) => l.getVisibleText())); - - expect(engineLinksText.includes(engine1.name)).to.equal(true); - expect(engineLinksText.includes(engine2.name)).to.equal(true); - }); - - it('lists meta engines', async () => { - const metaEngineLinks = await PageObjects.appSearch.getMetaEngineLinks(); - const metaEngineLinksText = await Promise.all( - metaEngineLinks.map((l) => l.getVisibleText()) - ); - expect(metaEngineLinksText.includes(metaEngine.name)).to.equal(true); - }); - }); - }); -} diff --git a/x-pack/test/functional_enterprise_search/artifact_manager.ts b/x-pack/test/functional_enterprise_search/artifact_manager.ts deleted file mode 100644 index 4dfccf16887d9..0000000000000 --- a/x-pack/test/functional_enterprise_search/artifact_manager.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import axios from 'axios'; -import { last } from 'lodash'; - -export async function getLatestVersion(): Promise { - const response: any = await axios('https://artifacts-api.elastic.co/v1/versions'); - return last(response.data.versions as string[]) || '8.7.0-SNAPSHOT'; -} diff --git a/x-pack/test/functional_enterprise_search/base_config.ts b/x-pack/test/functional_enterprise_search/base_config.ts deleted file mode 100644 index e5ea6fd616ca9..0000000000000 --- a/x-pack/test/functional_enterprise_search/base_config.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrConfigProviderContext } from '@kbn/test'; -import { pageObjects } from './page_objects'; -import { services } from './services'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const xPackFunctionalConfig = await readConfigFile( - require.resolve('../functional/config.base.js') - ); - - const kibanaCommonTestsConfig = await readConfigFile( - require.resolve('@kbn/test-suites-src/common/config') - ); - return { - // common test config - ...kibanaCommonTestsConfig.getAll(), - - // default to the xpack functional config - ...xPackFunctionalConfig.getAll(), - services, - pageObjects, - }; -} diff --git a/x-pack/test/functional_enterprise_search/cli_config.ts b/x-pack/test/functional_enterprise_search/cli_config.ts deleted file mode 100644 index ce439961848c8..0000000000000 --- a/x-pack/test/functional_enterprise_search/cli_config.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrConfigProviderContext } from '@kbn/test'; -import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; -import { commonFunctionalUIServices } from '@kbn/ftr-common-functional-ui-services'; -import { EnterpriseSearchCypressCliTestRunner } from './runner'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const kibanaCommonTestsConfig = await readConfigFile( - require.resolve('@kbn/test-suites-src/common/config') - ); - const baseConfig = await readConfigFile(require.resolve('./cypress.config')); - - return { - ...kibanaCommonTestsConfig.getAll(), - - services: { - ...commonFunctionalServices, - ...commonFunctionalUIServices, - }, - - // default to the xpack functional config - ...baseConfig.getAll(), - - junit: { - reportName: 'X-Pack Enterprise Search Functional Tests with Host Configured', - }, - kbnTestServer: { - ...baseConfig.get('kbnTestServer'), - serverArgs: [ - ...baseConfig.get('kbnTestServer.serverArgs'), - '--enterpriseSearch.host=http://localhost:3022', - ], - }, - testRunner: EnterpriseSearchCypressCliTestRunner, - }; -} diff --git a/x-pack/test/functional_enterprise_search/cypress.config.ts b/x-pack/test/functional_enterprise_search/cypress.config.ts deleted file mode 100644 index 099b92c429299..0000000000000 --- a/x-pack/test/functional_enterprise_search/cypress.config.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const baseConfig = await readConfigFile(require.resolve('./base_config')); - - return { - // default to the xpack functional config - ...baseConfig.getAll(), - - esTestCluster: { - ...baseConfig.get('esTestCluster'), - serverArgs: [ - ...baseConfig.get('esTestCluster.serverArgs'), - 'xpack.security.enabled=true', - 'xpack.security.authc.api_key.enabled=true', - ], - }, - - kbnTestServer: { - ...baseConfig.get('kbnTestServer'), - serverArgs: [ - ...baseConfig.get('kbnTestServer.serverArgs'), - '--csp.strict=false', - '--csp.warnLegacyBrowsers=false', - '--enterpriseSearch.host=http://localhost:3022', - '--usageCollection.uiCounters.enabled=false', - `--home.disableWelcomeScreen=true`, - ], - }, - }; -} diff --git a/x-pack/test/functional_enterprise_search/enterprise_search_server.ts b/x-pack/test/functional_enterprise_search/enterprise_search_server.ts deleted file mode 100644 index 4812912809433..0000000000000 --- a/x-pack/test/functional_enterprise_search/enterprise_search_server.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { spawn, ChildProcess } from 'child_process'; - -import { observeLines } from '@kbn/stdio-dev-helpers'; -import { ToolingLog } from '@kbn/tooling-log'; -import * as Rx from 'rxjs'; -import { filter, take, map, tap } from 'rxjs'; -import { getLatestVersion } from './artifact_manager'; - -let enterpriseSearchProcess: ChildProcess | undefined; - -const DOCKER_START_TIMEOUT = 5 * 60 * 1000; // 5 minutes -const dockerImage = `docker.elastic.co/enterprise-search/enterprise-search`; - -function firstWithTimeout(source$: Rx.Observable, errorMsg: string, ms = 30 * 1000) { - return Rx.race( - source$.pipe(take(1)), - Rx.timer(ms).pipe( - map(() => { - throw new Error(`[docker:${dockerImage}] ${errorMsg} within ${ms / 1000} seconds`); - }) - ) - ); -} - -function childProcessToLogLine(childProcess: ChildProcess, log: ToolingLog) { - const logLine$ = new Rx.Subject(); - - Rx.merge( - observeLines(childProcess.stdout!).pipe( - tap((line) => log.info(`[docker:${dockerImage}] ${line}`)) - ), - observeLines(childProcess.stderr!).pipe( - tap((line) => log.error(`[docker:${dockerImage}] ${line}`)) - ) - ).subscribe(logLine$); - - return logLine$.asObservable(); -} - -export async function setupEnterpriseSearch(logger: ToolingLog): Promise { - return new Promise(async (res, rej) => { - try { - const dockerArgs: string[] = [ - `run`, - `--name=enterprise-search-ftr`, - `--rm`, - `-p`, - `3022:3022`, - `-e`, - `elasticsearch.host='http://host.docker.internal:9220'`, - `-e`, - `elasticsearch.username=elastic`, - `-e`, - `elasticsearch.password=changeme`, - `-e`, - `allow_es_settings_modification=true`, - `-e`, - `secret_management.encryption_keys=[f8482eb76613714a62569a48f854d2390a957674d46db742c008d80745cd82d9]`, - `-e`, - `ENT_SEARCH_DEFAULT_PASSWORD=changeme`, - `-e`, - `ent_search.listen_port=3022`, - `-e`, - `ent_search.external_url='http://localhost:3022'`, - `docker.elastic.co/enterprise-search/enterprise-search:${await getLatestVersion()}`, - ]; - - logger.info('starting enterpriseSearch'); - logger.info('docker ' + dockerArgs.join(' ')); - - enterpriseSearchProcess = spawn('docker', dockerArgs, { stdio: ['ignore', 'pipe', 'pipe'] }); - enterpriseSearchProcess.on('error', rej); - - try { - await firstWithTimeout( - childProcessToLogLine(enterpriseSearchProcess, logger).pipe( - filter((line) => { - process.stdout.write(line); - return /Success! Elastic Enterprise Search is starting successfully./.test(line); - }) - ), - 'no package manifests loaded', - DOCKER_START_TIMEOUT - ).toPromise(); - } catch (err) { - enterpriseSearchProcess.kill(); - throw err; - } - setTimeout(res, 15000); - } catch (error) { - rej(error); - } - }); -} - -export function cleanupEnterpriseSearch(log: ToolingLog) { - if (enterpriseSearchProcess) { - log.info('Cleaning up Enterprise Search process'); - spawn('docker', ['stop', 'enterprise-search-ftr'], { stdio: 'inherit' }); - if (!enterpriseSearchProcess.kill(9)) { - log.info("Couldn't clean Enterprise Search process"); - } - - enterpriseSearchProcess.on('close', () => { - log.info('Enterprise Search closed '); - }); - } -} diff --git a/x-pack/test/functional_enterprise_search/page_objects/app_search.ts b/x-pack/test/functional_enterprise_search/page_objects/app_search.ts deleted file mode 100644 index 9ae967265d1b8..0000000000000 --- a/x-pack/test/functional_enterprise_search/page_objects/app_search.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; -import { TestSubjects } from '@kbn/ftr-common-functional-ui-services'; -import { FtrProviderContext } from '../ftr_provider_context'; - -export function AppSearchPageProvider({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['common']); - const testSubjects = getService('testSubjects') as TestSubjects; - - return { - async navigateToPage(): Promise { - return await PageObjects.common.navigateToApp('enterprise_search/app_search'); - }, - - async getEngineLinks(): Promise { - const engines = await testSubjects.find('appSearchEngines'); - return await testSubjects.findAllDescendant('EngineNameLink', engines); - }, - - async getMetaEngineLinks(): Promise { - const metaEngines = await testSubjects.find('appSearchMetaEngines'); - return await testSubjects.findAllDescendant('EngineNameLink', metaEngines); - }, - }; -} diff --git a/x-pack/test/functional_enterprise_search/page_objects/index.ts b/x-pack/test/functional_enterprise_search/page_objects/index.ts deleted file mode 100644 index 2d98718ccdeeb..0000000000000 --- a/x-pack/test/functional_enterprise_search/page_objects/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { pageObjects as basePageObjects } from '../../functional/page_objects'; -import { AppSearchPageProvider } from './app_search'; -import { WorkplaceSearchPageProvider } from './workplace_search'; - -export const pageObjects = { - ...basePageObjects, - appSearch: AppSearchPageProvider, - workplaceSearch: WorkplaceSearchPageProvider, -}; diff --git a/x-pack/test/functional_enterprise_search/page_objects/workplace_search.ts b/x-pack/test/functional_enterprise_search/page_objects/workplace_search.ts deleted file mode 100644 index 4d9aaa208ba59..0000000000000 --- a/x-pack/test/functional_enterprise_search/page_objects/workplace_search.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../ftr_provider_context'; - -export function WorkplaceSearchPageProvider({ getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['common']); - - return { - async navigateToPage(): Promise { - return await PageObjects.common.navigateToApp('enterprise_search/workplace_search'); - }, - }; -} diff --git a/x-pack/test/functional_enterprise_search/runner.ts b/x-pack/test/functional_enterprise_search/runner.ts deleted file mode 100644 index ac4098d2031d7..0000000000000 --- a/x-pack/test/functional_enterprise_search/runner.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { resolve } from 'path'; -import Url from 'url'; - -import { withProcRunner } from '@kbn/dev-proc-runner'; -import { setupEnterpriseSearch, cleanupEnterpriseSearch } from './enterprise_search_server'; - -import type { FtrProviderContext } from './ftr_provider_context'; - -export async function withEnterpriseSearch( - context: FtrProviderContext, - runner: (runnerEnv: Record) => Promise -) { - const log = context.getService('log'); - await setupEnterpriseSearch(log); - - try { - await runner({}); - } finally { - cleanupEnterpriseSearch(log); - } -} - -export async function runEnterpriseSearchTests( - context: FtrProviderContext, - cypressCommand: string -) { - const log = context.getService('log'); - const config = context.getService('config'); - await withEnterpriseSearch(context, (runnerEnv) => - withProcRunner(log, async (procs) => { - await procs.run('cypress', { - cmd: '../../../node_modules/.bin/cypress', - args: [cypressCommand, '--config-file', './cypress.config.ts', '--browser', 'chrome'], - cwd: resolve(__dirname, '../../solutions/search/plugins/enterprise_search'), - env: { - FORCE_COLOR: '1', - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_baseUrl: Url.format(config.get('servers.kibana')), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_protocol: config.get('servers.kibana.protocol'), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_hostname: config.get('servers.kibana.hostname'), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_configport: config.get('servers.kibana.port'), - CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), - CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), - CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), - CYPRESS_KIBANA_URL: Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - }), - ...runnerEnv, - ...process.env, - }, - wait: true, - }); - }) - ); -} - -export async function EnterpriseSearchCypressCliTestRunner(context: FtrProviderContext) { - await runEnterpriseSearchTests(context, 'run'); -} - -export async function EnterpriseSearchCypressVisualTestRunner(context: FtrProviderContext) { - await runEnterpriseSearchTests(context, 'open'); -} diff --git a/x-pack/test/functional_enterprise_search/services/app_search_client.ts b/x-pack/test/functional_enterprise_search/services/app_search_client.ts deleted file mode 100644 index 457523bccf8c3..0000000000000 --- a/x-pack/test/functional_enterprise_search/services/app_search_client.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import http from 'http'; - -/** - * A simple request client for making API calls to the App Search API - */ -const makeRequest = (method: string, path: string, body?: object): Promise => { - return new Promise(function (resolve, reject) { - const APP_SEARCH_API_KEY = process.env.APP_SEARCH_API_KEY; - - if (!APP_SEARCH_API_KEY) { - throw new Error('Please provide a valid APP_SEARCH_API_KEY. See README for more details.'); - } - - let postData; - - if (body) { - postData = JSON.stringify(body); - } - - const req = http.request( - { - method, - hostname: 'localhost', - port: 3002, - path, - agent: false, // Create a new agent just for this one request - headers: { - Authorization: `Bearer ${APP_SEARCH_API_KEY}`, - 'Content-Type': 'application/json', - ...(!!postData && { 'Content-Length': Buffer.byteLength(postData) }), - }, - }, - (res) => { - const bodyChunks: Uint8Array[] = []; - res.on('data', function (chunk) { - bodyChunks.push(chunk); - }); - - res.on('end', function () { - let responseBody; - try { - responseBody = JSON.parse(Buffer.concat(bodyChunks).toString()); - } catch (e) { - reject(e); - } - - if (res.statusCode && res.statusCode > 299) { - reject('Error calling App Search API: ' + JSON.stringify(responseBody)); - } - - resolve(responseBody); - }); - } - ); - - req.on('error', (e) => { - reject(e); - }); - - if (postData) { - req.write(postData); - } - req.end(); - }); -}; - -export interface IEngine { - name: string; -} - -export const createEngine = async (engineName: string): Promise => { - return await makeRequest('POST', '/api/as/v1/engines', { name: engineName }); -}; - -export const destroyEngine = async (engineName: string): Promise => { - return await makeRequest('DELETE', `/api/as/v1/engines/${engineName}`); -}; - -export const createMetaEngine = async ( - engineName: string, - sourceEngines: string[] -): Promise => { - return await makeRequest('POST', '/api/as/v1/engines', { - name: engineName, - type: 'meta', - source_engines: sourceEngines, - }); -}; - -export interface ISearchResponse { - results: object[]; -} - -const search = async (engineName: string): Promise => { - return await makeRequest('POST', `/api/as/v1/engines/${engineName}/search`, { query: '' }); -}; - -// Since the App Search API does not issue document receipts, the only way to tell whether or not documents -// are fully indexed is to poll the search endpoint. -export const waitForIndexedDocs = (engineName: string) => { - return new Promise(async function (resolve, reject) { - try { - let isReady = false; - - while (!isReady) { - const response = await search(engineName); - - if (response.results && response.results.length > 0) { - isReady = true; - resolve(); - } - } - } catch (error) { - reject(error); - } - }); -}; - -export const indexData = async (engineName: string, docs: object[]) => { - return await makeRequest('POST', `/api/as/v1/engines/${engineName}/documents`, docs); -}; diff --git a/x-pack/test/functional_enterprise_search/services/app_search_service.ts b/x-pack/test/functional_enterprise_search/services/app_search_service.ts deleted file mode 100644 index 95421fd1a4e4d..0000000000000 --- a/x-pack/test/functional_enterprise_search/services/app_search_service.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrProviderContext } from '../ftr_provider_context'; - -const ENTERPRISE_SEARCH_USER = 'enterprise_search'; -const ENTERPRISE_SEARCH_PASSWORD = 'changeme'; -import { - createEngine, - createMetaEngine, - indexData, - waitForIndexedDocs, - destroyEngine, - IEngine, -} from './app_search_client'; - -export interface IUser { - user: string; - password: string; -} -export type { IEngine }; - -export class AppSearchService { - getEnterpriseSearchUser(): IUser { - return { - user: ENTERPRISE_SEARCH_USER, - password: ENTERPRISE_SEARCH_PASSWORD, - }; - } - - createEngine(): Promise { - const engineName = `test-engine-${new Date().getTime()}`; - return createEngine(engineName); - } - - async createEngineWithDocs(): Promise { - const engine = await this.createEngine(); - const docs = [ - { id: 1, name: 'doc1' }, - { id: 2, name: 'doc2' }, - { id: 3, name: 'doc2' }, - ]; - await indexData(engine.name, docs); - await waitForIndexedDocs(engine.name); - return engine; - } - - async createMetaEngine(sourceEngines: string[]): Promise { - const engineName = `test-meta-engine-${new Date().getTime()}`; - return createMetaEngine(engineName, sourceEngines); - } - - async destroyEngine(engineName: string) { - return destroyEngine(engineName); - } -} - -export async function AppSearchServiceProvider({ getService }: FtrProviderContext) { - const lifecycle = getService('lifecycle'); - const security = getService('security'); - - lifecycle.beforeTests.add(async () => { - // The App Search plugin passes through the current user name and password - // through on the API call to App Search. Therefore, we need to be signed - // in as the enterprise_search user in order for this plugin to work. - await security.user.create(ENTERPRISE_SEARCH_USER, { - password: ENTERPRISE_SEARCH_PASSWORD, - roles: ['kibana_admin'], - full_name: ENTERPRISE_SEARCH_USER, - }); - }); - - return new AppSearchService(); -} diff --git a/x-pack/test/functional_enterprise_search/visual_config.ts b/x-pack/test/functional_enterprise_search/visual_config.ts deleted file mode 100644 index 3bbe099ac61ec..0000000000000 --- a/x-pack/test/functional_enterprise_search/visual_config.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { FtrConfigProviderContext } from '@kbn/test'; -import { EnterpriseSearchCypressVisualTestRunner } from './runner'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const kibanaCommonTestsConfig = await readConfigFile( - require.resolve('@kbn/test-suites-src/common/config') - ); - const baseConfig = await readConfigFile(require.resolve('./cypress.config')); - - return { - ...kibanaCommonTestsConfig.getAll(), - // default to the xpack functional config - ...baseConfig.getAll(), - - junit: { - reportName: 'X-Pack Enterprise Search Functional Tests with Host Configured', - }, - kbnTestServer: { - ...baseConfig.get('kbnTestServer'), - serverArgs: [ - ...baseConfig.get('kbnTestServer.serverArgs'), - '--enterpriseSearch.host=http://localhost:3022', - ], - }, - testRunner: EnterpriseSearchCypressVisualTestRunner, - }; -} diff --git a/x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics/data.json b/x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics/data.json new file mode 100644 index 0000000000000..5da2823731a44 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics/data.json @@ -0,0 +1,5073 @@ +{ + "type": "doc", + "value": { + "index": "metrics-endpoint.metrics-01", + "source": { + "agent": { + "build": { + "original": "version: 8.6.0, compiled: Mon Jan 2 23:00:00 2023, branch: 8.6, commit: e2d09ff1b8e49bfb5f8940d317eb4ac96672d956" + }, + "id": "456", + "type": "endpoint", + "version": "8.6.0" + }, + "@timestamp": "2024-01-26T14:38:30.628421712Z", + "Endpoint": { + "metrics": { + "system_impact": [ + { + "process": { + "executable": "/usr/bin/amazon-ssm-agent" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 36 + }, + "process_events": { + "week_idle_ms": 5, + "week_ms": 12487 + }, + "overall": { + "week_idle_ms": 5, + "week_ms": 12523 + } + }, + { + "process": { + "executable": "/usr/bin/ps" + }, + "process_events": { + "week_idle_ms": 8692, + "week_ms": 10288 + }, + "overall": { + "week_idle_ms": 8692, + "week_ms": 10288 + } + }, + { + "process": { + "executable": "/usr/sbin/sshd" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 116 + }, + "process_events": { + "week_idle_ms": 3621, + "week_ms": 7204 + }, + "network_events": { + "week_idle_ms": 97, + "week_ms": 0 + }, + "overall": { + "week_idle_ms": 3718, + "week_ms": 7320 + } + }, + { + "process": { + "executable": "/usr/sbin/dhclient-script" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 430 + }, + "process_events": { + "week_idle_ms": 152054, + "week_ms": 6229 + }, + "overall": { + "week_idle_ms": 152054, + "week_ms": 6659 + } + }, + { + "process": { + "executable": "/usr/sbin/crond" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 73 + }, + "process_events": { + "week_idle_ms": 9491, + "week_ms": 2060 + }, + "overall": { + "week_idle_ms": 9491, + "week_ms": 2133 + } + }, + { + "file_events": { + "week_idle_ms": 80958, + "week_ms": 1963 + }, + "process": { + "executable": "/usr/lib/systemd/systemd" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 23 + }, + "process_events": { + "week_idle_ms": 6, + "week_ms": 7 + }, + "overall": { + "week_idle_ms": 80964, + "week_ms": 1993 + } + }, + { + "process": { + "executable": "/bin/logger" + }, + "process_events": { + "week_idle_ms": 25156, + "week_ms": 1097 + }, + "overall": { + "week_idle_ms": 25156, + "week_ms": 1097 + } + }, + { + "file_events": { + "week_idle_ms": 12, + "week_ms": 0 + }, + "process": { + "executable": "/usr/lib64/sa/sadc" + }, + "process_events": { + "week_idle_ms": 4182, + "week_ms": 1005 + }, + "overall": { + "week_idle_ms": 4194, + "week_ms": 1005 + } + }, + { + "process": { + "executable": "unknown" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 981 + }, + "overall": { + "week_idle_ms": 0, + "week_ms": 981 + } + }, + { + "process": { + "executable": "/usr/bin/date" + }, + "process_events": { + "week_idle_ms": 8682, + "week_ms": 856 + }, + "overall": { + "week_idle_ms": 8682, + "week_ms": 856 + } + }, + { + "file_events": { + "week_idle_ms": 89949, + "week_ms": 835 + }, + "process": { + "executable": "/usr/lib/systemd/systemd-logind (deleted)" + }, + "overall": { + "week_idle_ms": 89949, + "week_ms": 835 + } + }, + { + "process": { + "executable": "/bin/cat" + }, + "process_events": { + "week_idle_ms": 28801, + "week_ms": 754 + }, + "overall": { + "week_idle_ms": 28801, + "week_ms": 754 + } + }, + { + "process": { + "executable": "/usr/lib64/sa/sa1" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 69 + }, + "process_events": { + "week_idle_ms": 7188, + "week_ms": 637 + }, + "overall": { + "week_idle_ms": 7188, + "week_ms": 706 + } + }, + { + "process": { + "executable": "/bin/cut" + }, + "process_events": { + "week_idle_ms": 7258, + "week_ms": 660 + }, + "overall": { + "week_idle_ms": 7258, + "week_ms": 660 + } + }, + { + "process": { + "executable": "/bin/ipcalc" + }, + "process_events": { + "week_idle_ms": 6603, + "week_ms": 477 + }, + "overall": { + "week_idle_ms": 6603, + "week_ms": 477 + } + }, + { + "process": { + "executable": "/sbin/ip" + }, + "process_events": { + "week_idle_ms": 14654, + "week_ms": 470 + }, + "overall": { + "week_idle_ms": 14654, + "week_ms": 470 + } + }, + { + "process": { + "executable": "/bin/curl" + }, + "process_events": { + "week_idle_ms": 4696, + "week_ms": 432 + }, + "network_events": { + "week_idle_ms": 3120, + "week_ms": 20 + }, + "overall": { + "week_idle_ms": 7816, + "week_ms": 452 + } + }, + { + "process": { + "executable": "/bin/run-parts" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 66 + }, + "process_events": { + "week_idle_ms": 4219, + "week_ms": 354 + }, + "overall": { + "week_idle_ms": 4219, + "week_ms": 420 + } + }, + { + "process": { + "executable": "/usr/sbin/dhclient" + }, + "process_events": { + "week_idle_ms": 3, + "week_ms": 326 + }, + "overall": { + "week_idle_ms": 3, + "week_ms": 326 + } + }, + { + "process": { + "executable": "/bin/sh" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 51 + }, + "process_events": { + "week_idle_ms": 2569, + "week_ms": 239 + }, + "overall": { + "week_idle_ms": 2569, + "week_ms": 290 + } + } + ], + "memory": { + "endpoint": { + "private": { + "mean": 119318921, + "latest": 136073216 + } + } + }, + "disks": [ + { + "total": 0, + "free": 0, + "device": "sysfs", + "mount": "/sys", + "fstype": "sysfs" + }, + { + "total": 0, + "free": 0, + "device": "proc", + "mount": "/proc", + "fstype": "proc" + }, + { + "total": 4157120512, + "free": 4157120512, + "device": "devtmpfs", + "mount": "/dev", + "fstype": "devtmpfs" + }, + { + "total": 0, + "free": 0, + "device": "securityfs", + "mount": "/sys/kernel/security", + "fstype": "securityfs" + }, + { + "total": 4166328320, + "free": 4166328320, + "device": "tmpfs", + "mount": "/dev/shm", + "fstype": "tmpfs" + }, + { + "total": 0, + "free": 0, + "device": "devpts", + "mount": "/dev/pts", + "fstype": "devpts" + }, + { + "total": 4166328320, + "free": 4165955584, + "device": "tmpfs", + "mount": "/run", + "fstype": "tmpfs" + }, + { + "total": 4166328320, + "free": 4166328320, + "device": "tmpfs", + "mount": "/sys/fs/cgroup", + "fstype": "tmpfs" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/systemd", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "pstore", + "mount": "/sys/fs/pstore", + "fstype": "pstore" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/devices", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpu,cpuacct", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/perf_event", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/pids", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/blkio", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/net_cls,net_prio", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/freezer", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/memory", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpuset", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/hugetlb", + "fstype": "cgroup" + }, + { + "endpoint_drive": true, + "total": 8577331200, + "free": 1841799168, + "device": "/dev/xvda1", + "mount": "/", + "fstype": "xfs" + }, + { + "total": 0, + "free": 0, + "device": "mqueue", + "mount": "/dev/mqueue", + "fstype": "mqueue" + }, + { + "total": 0, + "free": 0, + "device": "hugetlbfs", + "mount": "/dev/hugepages", + "fstype": "hugetlbfs" + }, + { + "total": 0, + "free": 0, + "device": "debugfs", + "mount": "/sys/kernel/debug", + "fstype": "debugfs" + }, + { + "total": 0, + "free": 0, + "device": "sunrpc", + "mount": "/var/lib/nfs/rpc_pipefs", + "fstype": "rpc_pipefs" + }, + { + "total": 0, + "free": 0, + "device": "systemd-1", + "mount": "/proc/sys/fs/binfmt_misc", + "fstype": "autofs" + }, + { + "total": 0, + "free": 0, + "device": "binfmt_misc", + "mount": "/proc/sys/fs/binfmt_misc", + "fstype": "binfmt_misc" + }, + { + "total": 0, + "free": 0, + "device": "tracefs", + "mount": "/sys/kernel/debug/tracing", + "fstype": "tracefs" + }, + { + "total": 0, + "free": 0, + "device": "none", + "mount": "/sys/fs/bpf", + "fstype": "bpf" + } + ], + "documents_volume": { + "file_events": { + "suppressed_count": 3928398, + "suppressed_bytes": 0, + "sent_count": 0, + "sent_bytes": 0 + }, + "process_events": { + "suppressed_count": 0, + "suppressed_bytes": 0, + "sent_count": 5689500, + "sent_bytes": 12905842838 + }, + "network_events": { + "suppressed_count": 23529207, + "suppressed_bytes": 0, + "sent_count": 0, + "sent_bytes": 0 + }, + "overall": { + "suppressed_count": 27457605, + "suppressed_bytes": 0, + "sent_count": 5689500, + "sent_bytes": 12905842838 + } + }, + "malicious_behavior_rules": [ + { + "id": "123", + "endpoint_uptime_percent": 0 + } + ], + "cpu": { + "endpoint": { + "histogram": { + "counts": [ + 3439059, + 129, + 15, + 0, + 5, + 6, + 4, + 0, + 3, + 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 85, + 90, + 95, + 100 + ] + }, + "mean": 0.2374646952923115, + "latest": 0.1 + } + }, + "threads": [ + { + "name": "Cron", + "cpu": { + "mean": 0.00252963994365742 + } + }, + { + "name": "FileLogThread", + "cpu": { + "mean": 0.0006221101490102125 + } + }, + { + "name": "LoggingLimitThread", + "cpu": { + "mean": 0.0003625542590258192 + } + }, + { + "name": "DocumentLoggingThread", + "cpu": { + "mean": 0.001359578471346822 + } + }, + { + "name": "DocumentLoggingMaintenance", + "cpu": { + "mean": 0.0001029983690414259 + } + }, + { + "name": "BulkConsumerThread", + "cpu": { + "mean": 0.009533529038474382 + } + }, + { + "name": "DocumentLoggingConsumerThread", + "cpu": { + "mean": 0.1315907162873257 + } + }, + { + "name": "DocumentLoggingLimitThread", + "cpu": { + "mean": 0.0002471960856994222 + } + }, + { + "name": "ArtifactManifestDownload", + "cpu": { + "mean": 0.001561455274668017 + } + }, + { + "name": "PolicyReloadThread", + "cpu": { + "mean": 0.00001235980428497111 + } + }, + { + "name": "PerformanceMonitorWorkerThread", + "cpu": { + "mean": 0.004639046541625823 + } + }, + { + "name": "MetadataThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "EventsQueueThread", + "cpu": { + "mean": 0.05644722616946305 + } + }, + { + "name": "DelayedAlertEnrichment", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "MaintainProcessMap", + "cpu": { + "mean": 0.002167085684631601 + } + }, + { + "name": "FileScoreThread", + "cpu": { + "mean": 0.002241244510341427 + } + }, + { + "name": "DiagnosticMalwareThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "QuarantineManagerWorkerThread", + "cpu": { + "mean": 0.002014648098450291 + } + }, + { + "name": "EventProcessingThread", + "cpu": { + "mean": 0.00583794755726802 + } + }, + { + "name": "HostIsolationMonitorThread", + "cpu": { + "mean": 0.0008281068870930642 + } + }, + { + "name": "MountMonitor", + "cpu": { + "mean": 0.003407186047890369 + } + }, + { + "name": "responseActionsUploadThread", + "cpu": { + "mean": 0.00208468698939846 + } + }, + { + "name": "responseActionsProcessThread", + "cpu": { + "mean": 0.002105286663206745 + } + }, + { + "name": "serviceCommsThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "grpcConnectionManagerThread", + "cpu": { + "mean": 0.002344242879382854 + } + }, + { + "name": "checkinAPIThread", + "cpu": { + "mean": 0.0002513162482505196 + } + }, + { + "name": "actionsAPIThread", + "cpu": { + "mean": 0.0004985125580051291 + } + }, + { + "name": "stateReportThread", + "cpu": { + "mean": 0.004400094313632049 + } + }, + { + "name": "EventsLoopThread", + "cpu": { + "mean": 0.008866112391817567 + } + }, + { + "name": "FanotifyWatchdog", + "cpu": { + "mean": 0.0002142369165309078 + } + }, + { + "name": "FanotifySyncConsumer", + "cpu": { + "mean": 0.001042344997736917 + } + }, + { + "name": "FanotifyAsyncConsumer", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "FanotifyConsumer", + "cpu": { + "mean": 0.03632551717409642 + } + }, + { + "name": "RulesEngineThread", + "cpu": { + "mean": 0.03752853985923151 + } + } + ], + "event_filter": { + "active_global_count": 0, + "active_user_count": 0 + }, + "uptime": { + "endpoint": 24272228, + "system": 29719735 + } + } + }, + "ecs": { + "version": "1.11.0" + }, + "data_stream": { + "namespace": "default", + "type": "metrics", + "dataset": "endpoint.metrics" + }, + "elastic": { + "agent": { + "id": "123" + } + }, + "host": { + "hostname": "123", + "os": { + "Ext": { + "variant": "Amazon Linux" + }, + "kernel": "5.10.102-99.473.amzn2.x86_64 #1 SMP Wed Mar 2 19:14:12 UTC 2022", + "name": "Linux", + "family": "amazon linux", + "type": "linux", + "version": "2", + "platform": "amazon linux", + "full": "Amazon Linux 2" + }, + "ip": [ + "127.0.0.1", + "::1" + ], + "name": "123", + "id": "123", + "mac": [ + "aa:aa:aa:aa:aa:aa" + ], + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "sequence": 33235745, + "ingested": "2024-01-26T14:38:32Z", + "created": "2024-01-26T14:38:30.628421712Z", + "kind": "metric", + "module": "endpoint", + "action": "endpoint_metrics", + "id": "123", + "category": [ + "host" + ], + "type": [ + "info" + ], + "dataset": "endpoint.metrics" + }, + "message": "Endpoint metrics" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "index": "metrics-endpoint.metrics-01", + "source": { + "agent": { + "build": { + "original": "version: 8.6.0, compiled: Mon Jan 2 23:00:00 2023, branch: 8.6, commit: e2d09ff1b8e49bfb5f8940d317eb4ac96672d956" + }, + "id": "123", + "type": "endpoint", + "version": "8.6.0" + }, + "@timestamp": "2024-01-26T14:35:43.564911752Z", + "Endpoint": { + "metrics": { + "system_impact": [ + { + "process": { + "executable": "/usr/bin/amazon-ssm-agent" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 51 + }, + "process_events": { + "week_idle_ms": 2, + "week_ms": 8914 + }, + "overall": { + "week_idle_ms": 2, + "week_ms": 8965 + } + }, + { + "process": { + "executable": "/usr/bin/ps" + }, + "process_events": { + "week_idle_ms": 7543, + "week_ms": 6667 + }, + "overall": { + "week_idle_ms": 7543, + "week_ms": 6667 + } + }, + { + "process": { + "executable": "/usr/sbin/sshd" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 51 + }, + "process_events": { + "week_idle_ms": 3596, + "week_ms": 4596 + }, + "network_events": { + "week_idle_ms": 85, + "week_ms": 5 + }, + "overall": { + "week_idle_ms": 3681, + "week_ms": 4652 + } + }, + { + "process": { + "executable": "/usr/sbin/dhclient-script" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 380 + }, + "process_events": { + "week_idle_ms": 105790, + "week_ms": 3877 + }, + "overall": { + "week_idle_ms": 105790, + "week_ms": 4257 + } + }, + { + "process": { + "executable": "/usr/sbin/crond" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 129 + }, + "process_events": { + "week_idle_ms": 8062, + "week_ms": 1903 + }, + "overall": { + "week_idle_ms": 8062, + "week_ms": 2032 + } + }, + { + "file_events": { + "week_idle_ms": 69051, + "week_ms": 1544 + }, + "process": { + "executable": "/usr/lib/systemd/systemd" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 15 + }, + "process_events": { + "week_idle_ms": 31, + "week_ms": 9 + }, + "overall": { + "week_idle_ms": 69082, + "week_ms": 1568 + } + }, + { + "process": { + "executable": "unknown" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 979 + }, + "overall": { + "week_idle_ms": 0, + "week_ms": 979 + } + }, + { + "file_events": { + "week_idle_ms": 29, + "week_ms": 0 + }, + "process": { + "executable": "/usr/lib64/sa/sadc" + }, + "process_events": { + "week_idle_ms": 5107, + "week_ms": 917 + }, + "overall": { + "week_idle_ms": 5136, + "week_ms": 917 + } + }, + { + "process": { + "executable": "/bin/logger" + }, + "process_events": { + "week_idle_ms": 20585, + "week_ms": 880 + }, + "overall": { + "week_idle_ms": 20585, + "week_ms": 880 + } + }, + { + "file_events": { + "week_idle_ms": 72520, + "week_ms": 852 + }, + "process": { + "executable": "/usr/lib/systemd/systemd-logind (deleted)" + }, + "overall": { + "week_idle_ms": 72520, + "week_ms": 852 + } + }, + { + "process": { + "executable": "/usr/bin/date" + }, + "process_events": { + "week_idle_ms": 9862, + "week_ms": 808 + }, + "overall": { + "week_idle_ms": 9862, + "week_ms": 808 + } + }, + { + "process": { + "executable": "/bin/cat" + }, + "process_events": { + "week_idle_ms": 17468, + "week_ms": 701 + }, + "overall": { + "week_idle_ms": 17468, + "week_ms": 701 + } + }, + { + "process": { + "executable": "/usr/lib64/sa/sa1" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 67 + }, + "process_events": { + "week_idle_ms": 7451, + "week_ms": 618 + }, + "overall": { + "week_idle_ms": 7451, + "week_ms": 685 + } + }, + { + "process": { + "executable": "/bin/curl" + }, + "process_events": { + "week_idle_ms": 5615, + "week_ms": 403 + }, + "network_events": { + "week_idle_ms": 4128, + "week_ms": 6 + }, + "overall": { + "week_idle_ms": 9743, + "week_ms": 409 + } + }, + { + "process": { + "executable": "/bin/run-parts" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 116 + }, + "process_events": { + "week_idle_ms": 3770, + "week_ms": 260 + }, + "overall": { + "week_idle_ms": 3770, + "week_ms": 376 + } + }, + { + "process": { + "executable": "/usr/sbin/dhclient" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 36 + }, + "process_events": { + "week_idle_ms": 1, + "week_ms": 278 + }, + "overall": { + "week_idle_ms": 1, + "week_ms": 314 + } + }, + { + "process": { + "executable": "/bin/cut" + }, + "process_events": { + "week_idle_ms": 5067, + "week_ms": 313 + }, + "overall": { + "week_idle_ms": 5067, + "week_ms": 313 + } + }, + { + "process": { + "executable": "/sbin/ip" + }, + "process_events": { + "week_idle_ms": 10369, + "week_ms": 286 + }, + "overall": { + "week_idle_ms": 10369, + "week_ms": 286 + } + }, + { + "process": { + "executable": "/bin/sh" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 20 + }, + "process_events": { + "week_idle_ms": 2397, + "week_ms": 208 + }, + "overall": { + "week_idle_ms": 2397, + "week_ms": 228 + } + }, + { + "process": { + "executable": "/bin/ipcalc" + }, + "process_events": { + "week_idle_ms": 4827, + "week_ms": 201 + }, + "overall": { + "week_idle_ms": 4827, + "week_ms": 201 + } + } + ], + "memory": { + "endpoint": { + "private": { + "mean": 118266395, + "latest": 135163904 + } + } + }, + "disks": [ + { + "total": 0, + "free": 0, + "device": "sysfs", + "mount": "/sys", + "fstype": "sysfs" + }, + { + "total": 0, + "free": 0, + "device": "proc", + "mount": "/proc", + "fstype": "proc" + }, + { + "total": 4157120512, + "free": 4157120512, + "device": "devtmpfs", + "mount": "/dev", + "fstype": "devtmpfs" + }, + { + "total": 0, + "free": 0, + "device": "securityfs", + "mount": "/sys/kernel/security", + "fstype": "securityfs" + }, + { + "total": 4166328320, + "free": 4166328320, + "device": "tmpfs", + "mount": "/dev/shm", + "fstype": "tmpfs" + }, + { + "total": 0, + "free": 0, + "device": "devpts", + "mount": "/dev/pts", + "fstype": "devpts" + }, + { + "total": 4166328320, + "free": 4165955584, + "device": "tmpfs", + "mount": "/run", + "fstype": "tmpfs" + }, + { + "total": 4166328320, + "free": 4166328320, + "device": "tmpfs", + "mount": "/sys/fs/cgroup", + "fstype": "tmpfs" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/systemd", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "pstore", + "mount": "/sys/fs/pstore", + "fstype": "pstore" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/blkio", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpu,cpuacct", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/net_cls,net_prio", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/devices", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/cpuset", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/hugetlb", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/pids", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/freezer", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/memory", + "fstype": "cgroup" + }, + { + "total": 0, + "free": 0, + "device": "cgroup", + "mount": "/sys/fs/cgroup/perf_event", + "fstype": "cgroup" + }, + { + "endpoint_drive": true, + "total": 8577331200, + "free": 1908756480, + "device": "/dev/xvda1", + "mount": "/", + "fstype": "xfs" + }, + { + "total": 0, + "free": 0, + "device": "hugetlbfs", + "mount": "/dev/hugepages", + "fstype": "hugetlbfs" + }, + { + "total": 0, + "free": 0, + "device": "mqueue", + "mount": "/dev/mqueue", + "fstype": "mqueue" + }, + { + "total": 0, + "free": 0, + "device": "debugfs", + "mount": "/sys/kernel/debug", + "fstype": "debugfs" + }, + { + "total": 0, + "free": 0, + "device": "sunrpc", + "mount": "/var/lib/nfs/rpc_pipefs", + "fstype": "rpc_pipefs" + }, + { + "total": 0, + "free": 0, + "device": "systemd-1", + "mount": "/proc/sys/fs/binfmt_misc", + "fstype": "autofs" + }, + { + "total": 0, + "free": 0, + "device": "binfmt_misc", + "mount": "/proc/sys/fs/binfmt_misc", + "fstype": "binfmt_misc" + }, + { + "total": 0, + "free": 0, + "device": "tracefs", + "mount": "/sys/kernel/debug/tracing", + "fstype": "tracefs" + }, + { + "total": 0, + "free": 0, + "device": "none", + "mount": "/sys/fs/bpf", + "fstype": "bpf" + } + ], + "documents_volume": { + "file_events": { + "suppressed_count": 3926292, + "suppressed_bytes": 0, + "sent_count": 0, + "sent_bytes": 0 + }, + "process_events": { + "suppressed_count": 0, + "suppressed_bytes": 0, + "sent_count": 5737776, + "sent_bytes": 12992145486 + }, + "network_events": { + "suppressed_count": 24813200, + "suppressed_bytes": 0, + "sent_count": 0, + "sent_bytes": 0 + }, + "overall": { + "suppressed_count": 28739492, + "suppressed_bytes": 0, + "sent_count": 5737776, + "sent_bytes": 12992145486 + } + }, + "malicious_behavior_rules": [ + { + "id": "123", + "endpoint_uptime_percent": 0 + } + ], + "cpu": { + "endpoint": { + "histogram": { + "counts": [ + 3147842, + 87, + 11, + 10, + 9, + 5, + 10, + 6, + 1, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 85, + 90, + 95, + 100 + ] + }, + "mean": 0.2338436071605115, + "latest": 0.9000000000000001 + } + }, + "threads": [ + { + "name": "Cron", + "cpu": { + "mean": 0.002129690752013577 + } + }, + { + "name": "FileLogThread", + "cpu": { + "mean": 0.0004778416387496615 + } + }, + { + "name": "LoggingLimitThread", + "cpu": { + "mean": 0.0002965913619825484 + } + }, + { + "name": "DocumentLoggingThread", + "cpu": { + "mean": 0.001116336931906537 + } + }, + { + "name": "DocumentLoggingMaintenance", + "cpu": { + "mean": 0.00009062513838355648 + } + }, + { + "name": "BulkConsumerThread", + "cpu": { + "mean": 0.008971888699972091 + } + }, + { + "name": "DocumentLoggingConsumerThread", + "cpu": { + "mean": 0.1281974968924846 + } + }, + { + "name": "DocumentLoggingLimitThread", + "cpu": { + "mean": 0.000205966223598992 + } + }, + { + "name": "ArtifactManifestDownload", + "cpu": { + "mean": 0.001515911405688581 + } + }, + { + "name": "PolicyReloadThread", + "cpu": { + "mean": 0.00000823864894395968 + } + }, + { + "name": "PerformanceMonitorWorkerThread", + "cpu": { + "mean": 0.003917477572852828 + } + }, + { + "name": "MetadataThread", + "cpu": { + "mean": 0.00000411932447197984 + } + }, + { + "name": "EventsQueueThread", + "cpu": { + "mean": 0.04977791691940438 + } + }, + { + "name": "DelayedAlertEnrichment", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "MaintainProcessMap", + "cpu": { + "mean": 0.001837218714503009 + } + }, + { + "name": "FileScoreThread", + "cpu": { + "mean": 0.001923724528414585 + } + }, + { + "name": "DiagnosticMalwareThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "QuarantineManagerWorkerThread", + "cpu": { + "mean": 0.001557104650408379 + } + }, + { + "name": "EventProcessingThread", + "cpu": { + "mean": 0.00591534994176305 + } + }, + { + "name": "HostIsolationMonitorThread", + "cpu": { + "mean": 0.0006508532665728148 + } + }, + { + "name": "MountMonitor", + "cpu": { + "mean": 0.002533384550267602 + } + }, + { + "name": "responseActionsUploadThread", + "cpu": { + "mean": 0.001771309522951331 + } + }, + { + "name": "responseActionsProcessThread", + "cpu": { + "mean": 0.001783667496367271 + } + }, + { + "name": "serviceCommsThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "grpcConnectionManagerThread", + "cpu": { + "mean": 0.001956679124190424 + } + }, + { + "name": "checkinAPIThread", + "cpu": { + "mean": 0.0002389210358973501 + } + }, + { + "name": "actionsAPIThread", + "cpu": { + "mean": 0.0003913361794870389 + } + }, + { + "name": "stateReportThread", + "cpu": { + "mean": 0.003550860912819238 + } + }, + { + "name": "EventsLoopThread", + "cpu": { + "mean": 0.0077690568352644 + } + }, + { + "name": "FanotifyWatchdog", + "cpu": { + "mean": 0.0001647732096556607 + } + }, + { + "name": "FanotifySyncConsumer", + "cpu": { + "mean": 0.001103980504692926 + } + }, + { + "name": "FanotifyAsyncConsumer", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "FanotifyConsumer", + "cpu": { + "mean": 0.02717934093270123 + } + }, + { + "name": "RulesEngineThread", + "cpu": { + "mean": 0.03744471189424889 + } + } + ], + "event_filter": { + "active_global_count": 0, + "active_user_count": 0 + }, + "uptime": { + "endpoint": 24275825, + "system": 29714870 + } + } + }, + "ecs": { + "version": "1.11.0" + }, + "data_stream": { + "namespace": "default", + "type": "metrics", + "dataset": "endpoint.metrics" + }, + "elastic": { + "agent": { + "id": "123" + } + }, + "host": { + "hostname": "123", + "os": { + "Ext": { + "variant": "Amazon Linux" + }, + "kernel": "5.10.102-99.473.amzn2.x86_64 #1 SMP Wed Mar 2 19:14:12 UTC 2022", + "name": "Linux", + "family": "amazon linux", + "type": "linux", + "version": "2", + "platform": "amazon linux", + "full": "Amazon Linux 2" + }, + "ip": [ + "127.0.0.1", + "::1" + ], + "name": "123", + "id": "123", + "mac": [ + "aa:aa:aa:aa:aa:aa" + ], + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "sequence": 34569822, + "ingested": "2024-01-26T14:35:45Z", + "created": "2024-01-26T14:35:43.564911752Z", + "kind": "metric", + "module": "endpoint", + "action": "endpoint_metrics", + "id": "123", + "category": [ + "host" + ], + "type": [ + "info" + ], + "dataset": "endpoint.metrics" + }, + "message": "Endpoint metrics" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "index": "metrics-endpoint.metrics-01", + "source": { + "agent": { + "build": { + "original": "version: 8.6.2, compiled: Fri Feb 10 14:00:00 2023, branch: 8.6, commit: eaee25e73cc58a52387e50d5bce542ec8965f638" + }, + "id": "123", + "type": "endpoint", + "version": "8.6.2" + }, + "@timestamp": "2024-01-26T15:06:50.254822749Z", + "Endpoint": { + "metrics": { + "system_impact": [ + { + "process": { + "executable": "/usr/sbin/sshd" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 304 + }, + "process_events": { + "week_idle_ms": 31791, + "week_ms": 110821 + }, + "network_events": { + "week_idle_ms": 2030, + "week_ms": 53 + }, + "overall": { + "week_idle_ms": 33821, + "week_ms": 111178 + } + }, + { + "process": { + "executable": "unknown" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 4552 + }, + "overall": { + "week_idle_ms": 0, + "week_ms": 4552 + } + }, + { + "file_events": { + "week_idle_ms": 130, + "week_ms": 7 + }, + "process": { + "executable": "/lib/systemd/systemd" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 1920 + }, + "process_events": { + "week_idle_ms": 1273, + "week_ms": 1750 + }, + "overall": { + "week_idle_ms": 1403, + "week_ms": 3677 + } + }, + { + "file_events": { + "week_idle_ms": 7, + "week_ms": 0 + }, + "process": { + "executable": "/bin/systemctl" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 3055 + }, + "process_events": { + "week_idle_ms": 0, + "week_ms": 9 + }, + "overall": { + "week_idle_ms": 7, + "week_ms": 3064 + } + }, + { + "file_events": { + "week_idle_ms": 3104, + "week_ms": 1 + }, + "process": { + "executable": "/usr/bin/apt-key" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 61 + }, + "process_events": { + "week_idle_ms": 178857, + "week_ms": 2062 + }, + "overall": { + "week_idle_ms": 181961, + "week_ms": 2124 + } + }, + { + "process": { + "executable": "/usr/bin/gce_workload_cert_refresh" + }, + "process_events": { + "week_idle_ms": 230, + "week_ms": 1405 + }, + "network_events": { + "week_idle_ms": 135, + "week_ms": 21 + }, + "overall": { + "week_idle_ms": 365, + "week_ms": 1426 + } + }, + { + "process": { + "executable": "/usr/bin/apt-config" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 65 + }, + "process_events": { + "week_idle_ms": 18708, + "week_ms": 840 + }, + "overall": { + "week_idle_ms": 18708, + "week_ms": 905 + } + }, + { + "process": { + "executable": "/usr/lib/apt/apt.systemd.daily" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 273 + }, + "process_events": { + "week_idle_ms": 1064, + "week_ms": 352 + }, + "overall": { + "week_idle_ms": 1064, + "week_ms": 625 + } + }, + { + "process": { + "executable": "/usr/bin/dpkg" + }, + "process_events": { + "week_idle_ms": 11479, + "week_ms": 558 + }, + "overall": { + "week_idle_ms": 11479, + "week_ms": 558 + } + }, + { + "process": { + "executable": "/usr/sbin/cron" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 80 + }, + "process_events": { + "week_idle_ms": 270, + "week_ms": 451 + }, + "overall": { + "week_idle_ms": 270, + "week_ms": 531 + } + }, + { + "process": { + "executable": "/usr/bin/cmp" + }, + "process_events": { + "week_idle_ms": 39452, + "week_ms": 513 + }, + "overall": { + "week_idle_ms": 39452, + "week_ms": 513 + } + }, + { + "file_events": { + "week_idle_ms": 144, + "week_ms": 0 + }, + "process": { + "executable": "/sbin/dhclient-script" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 251 + }, + "process_events": { + "week_idle_ms": 2412, + "week_ms": 186 + }, + "overall": { + "week_idle_ms": 2556, + "week_ms": 437 + } + }, + { + "process": { + "executable": "/usr/bin/env" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 413 + }, + "process_events": { + "week_idle_ms": 45, + "week_ms": 4 + }, + "overall": { + "week_idle_ms": 45, + "week_ms": 417 + } + }, + { + "process": { + "executable": "/usr/bin/cat" + }, + "process_events": { + "week_idle_ms": 39679, + "week_ms": 359 + }, + "overall": { + "week_idle_ms": 39679, + "week_ms": 359 + } + }, + { + "process": { + "executable": "/bin/sh" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 35 + }, + "process_events": { + "week_idle_ms": 444, + "week_ms": 197 + }, + "overall": { + "week_idle_ms": 444, + "week_ms": 232 + } + }, + { + "process": { + "executable": "/usr/lib/apt/apt-helper" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 100 + }, + "process_events": { + "week_idle_ms": 8, + "week_ms": 62 + }, + "overall": { + "week_idle_ms": 8, + "week_ms": 162 + } + }, + { + "process": { + "executable": "/usr/bin/date" + }, + "process_events": { + "week_idle_ms": 1024, + "week_ms": 143 + }, + "overall": { + "week_idle_ms": 1024, + "week_ms": 143 + } + }, + { + "file_events": { + "week_idle_ms": 5315, + "week_ms": 2 + }, + "process": { + "executable": "/usr/bin/apt-get" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 53 + }, + "process_events": { + "week_idle_ms": 576, + "week_ms": 76 + }, + "overall": { + "week_idle_ms": 5891, + "week_ms": 131 + } + }, + { + "process": { + "executable": "/usr/bin/unattended-upgrade" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 27 + }, + "process_events": { + "week_idle_ms": 139, + "week_ms": 95 + }, + "overall": { + "week_idle_ms": 139, + "week_ms": 122 + } + }, + { + "process": { + "executable": "/usr/bin/gpgconf" + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 26 + }, + "process_events": { + "week_idle_ms": 12075, + "week_ms": 93 + }, + "overall": { + "week_idle_ms": 12075, + "week_ms": 119 + } + } + ], + "memory": { + "endpoint": { + "private": { + "mean": 373780821, + "latest": 411860992 + } + } + }, + "disks": [ + { + "total": 0, + "free": 0, + "device": "sysfs", + "mount": "/sys", + "fstype": "sysfs" + }, + { + "total": 0, + "free": 0, + "device": "proc", + "mount": "/proc", + "fstype": "proc" + }, + { + "total": 2049777664, + "free": 2049777664, + "device": "udev", + "mount": "/dev", + "fstype": "devtmpfs" + }, + { + "total": 0, + "free": 0, + "device": "devpts", + "mount": "/dev/pts", + "fstype": "devpts" + }, + { + "total": 412172288, + "free": 411795456, + "device": "tmpfs", + "mount": "/run", + "fstype": "tmpfs" + }, + { + "endpoint_drive": true, + "total": 10331889664, + "free": 4091109376, + "device": "/dev/sda1", + "mount": "/", + "fstype": "ext4" + }, + { + "total": 0, + "free": 0, + "device": "securityfs", + "mount": "/sys/kernel/security", + "fstype": "securityfs" + }, + { + "total": 2060849152, + "free": 2060849152, + "device": "tmpfs", + "mount": "/dev/shm", + "fstype": "tmpfs" + }, + { + "total": 5242880, + "free": 5242880, + "device": "tmpfs", + "mount": "/run/lock", + "fstype": "tmpfs" + }, + { + "total": 0, + "free": 0, + "device": "cgroup2", + "mount": "/sys/fs/cgroup", + "fstype": "cgroup2" + }, + { + "total": 0, + "free": 0, + "device": "pstore", + "mount": "/sys/fs/pstore", + "fstype": "pstore" + }, + { + "total": 0, + "free": 0, + "device": "efivarfs", + "mount": "/sys/firmware/efi/efivars", + "fstype": "efivarfs" + }, + { + "total": 0, + "free": 0, + "device": "none", + "mount": "/sys/fs/bpf", + "fstype": "bpf" + }, + { + "total": 0, + "free": 0, + "device": "systemd-1", + "mount": "/proc/sys/fs/binfmt_misc", + "fstype": "autofs" + }, + { + "total": 0, + "free": 0, + "device": "hugetlbfs", + "mount": "/dev/hugepages", + "fstype": "hugetlbfs" + }, + { + "total": 0, + "free": 0, + "device": "mqueue", + "mount": "/dev/mqueue", + "fstype": "mqueue" + }, + { + "total": 0, + "free": 0, + "device": "debugfs", + "mount": "/sys/kernel/debug", + "fstype": "debugfs" + }, + { + "total": 0, + "free": 0, + "device": "tracefs", + "mount": "/sys/kernel/tracing", + "fstype": "tracefs" + }, + { + "total": 0, + "free": 0, + "device": "fusectl", + "mount": "/sys/fs/fuse/connections", + "fstype": "fusectl" + }, + { + "total": 0, + "free": 0, + "device": "configfs", + "mount": "/sys/kernel/config", + "fstype": "configfs" + }, + { + "total": 129751040, + "free": 118589440, + "device": "/dev/sda15", + "mount": "/boot/efi", + "fstype": "vfat" + }, + { + "total": 0, + "free": 0, + "device": "binfmt_misc", + "mount": "/proc/sys/fs/binfmt_misc", + "fstype": "binfmt_misc" + }, + { + "total": 0, + "free": 0, + "device": "tracefs", + "mount": "/sys/kernel/debug/tracing", + "fstype": "tracefs" + } + ], + "documents_volume": { + "file_events": { + "suppressed_count": 1651293, + "suppressed_bytes": 0, + "sent_count": 0, + "sent_bytes": 0 + }, + "process_events": { + "suppressed_count": 0, + "suppressed_bytes": 0, + "sent_count": 11026887, + "sent_bytes": 23412176619 + }, + "network_events": { + "suppressed_count": 3270009, + "suppressed_bytes": 0, + "sent_count": 0, + "sent_bytes": 0 + }, + "overall": { + "suppressed_count": 4921302, + "suppressed_bytes": 0, + "sent_count": 11026887, + "sent_bytes": 23412176619 + } + }, + "malicious_behavior_rules": [ + { + "id": "123", + "endpoint_uptime_percent": 0 + } + ], + "cpu": { + "endpoint": { + "histogram": { + "counts": [ + 3934326, + 520, + 140, + 110, + 212, + 78, + 41, + 18, + 12, + 25, + 9, + 5, + 5, + 2, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 85, + 90, + 95, + 100 + ] + }, + "mean": 0.2697424700222798, + "latest": 0.1 + } + }, + "threads": [ + { + "name": "Cron", + "cpu": { + "mean": 0.005330585583239717 + } + }, + { + "name": "FileLogThread", + "cpu": { + "mean": 0.01387886588350829 + } + }, + { + "name": "LoggingLimitThread", + "cpu": { + "mean": 0.0003385089239880071 + } + }, + { + "name": "DocumentLoggingThread", + "cpu": { + "mean": 0.002019893909071295 + } + }, + { + "name": "DocumentLoggingMaintenance", + "cpu": { + "mean": 0.0001711144011367948 + } + }, + { + "name": "BulkConsumerThread", + "cpu": { + "mean": 0.01437360969549076 + } + }, + { + "name": "DocumentLoggingConsumerThread", + "cpu": { + "mean": 0.1566068758230231 + } + }, + { + "name": "DocumentLoggingLimitThread", + "cpu": { + "mean": 0.000383147463414997 + } + }, + { + "name": "ArtifactManifestDownload", + "cpu": { + "mean": 0.00202733366564246 + } + }, + { + "name": "PolicyReloadThread", + "cpu": { + "mean": 0.00001487951314232998 + } + }, + { + "name": "PerformanceMonitorWorkerThread", + "cpu": { + "mean": 0.005423582540379278 + } + }, + { + "name": "MetadataThread", + "cpu": { + "mean": 0.00001115963485674749 + } + }, + { + "name": "EventsQueueThread", + "cpu": { + "mean": 0.05409447002894065 + } + }, + { + "name": "DelayedAlertEnrichment", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "MaintainProcessMap", + "cpu": { + "mean": 0.002905224941039929 + } + }, + { + "name": "FileScoreThread", + "cpu": { + "mean": 0.003567363275873613 + } + }, + { + "name": "DiagnosticMalwareThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "QuarantineManagerWorkerThread", + "cpu": { + "mean": 0.002083131839926198 + } + }, + { + "name": "EventProcessingThread", + "cpu": { + "mean": 0.01754294664738331 + } + }, + { + "name": "HostIsolationMonitorThread", + "cpu": { + "mean": 0.0008481322806622976 + } + }, + { + "name": "MountMonitor", + "cpu": { + "mean": 0.003463206812704382 + } + }, + { + "name": "responseActionsUploadThread", + "cpu": { + "mean": 0.00270807149264102 + } + }, + { + "name": "responseActionsProcessThread", + "cpu": { + "mean": 0.002689472100521233 + } + }, + { + "name": "serviceCommsThread", + "cpu": { + "mean": 0.1 + } + }, + { + "name": "grpcConnectionManagerThread", + "cpu": { + "mean": 0.0110926774602411 + } + }, + { + "name": "checkinAPIThread", + "cpu": { + "mean": 0.0003124700433295513 + } + }, + { + "name": "actionsAPIThread", + "cpu": { + "mean": 0.0004910243538035807 + } + }, + { + "name": "stateReportThread", + "cpu": { + "mean": 0.00539010824743476 + } + }, + { + "name": "EventsLoopThread", + "cpu": { + "mean": 0.003266057265515501 + } + }, + { + "name": "FanotifyWatchdog", + "cpu": { + "mean": 0.0002231929794201937 + } + }, + { + "name": "FanotifySyncConsumer", + "cpu": { + "mean": 0.001885980676100637 + } + }, + { + "name": "FanotifyAsyncConsumer", + "cpu": { + "mean": 0.0002492321603525497 + } + }, + { + "name": "FanotifyConsumer", + "cpu": { + "mean": 0.03793164685246193 + } + }, + { + "name": "RulesEngineThread", + "cpu": { + "mean": 0.02721466395730229 + } + } + ], + "event_filter": { + "active_global_count": 0, + "active_user_count": 0 + }, + "uptime": { + "endpoint": 26882599, + "system": 26883343 + } + } + }, + "ecs": { + "version": "1.11.0" + }, + "data_stream": { + "namespace": "default", + "type": "metrics", + "dataset": "endpoint.metrics" + }, + "elastic": { + "agent": { + "id": "123" + } + }, + "host": { + "hostname": "123", + "os": { + "Ext": { + "variant": "Debian" + }, + "kernel": "5.10.0-21-cloud-amd64 #1 SMP Debian 5.10.162-1 (2023-01-21)", + "name": "Linux", + "family": "debian", + "type": "linux", + "version": "11.8", + "platform": "debian", + "full": "Debian 11.8" + }, + "ip": [ + "127.0.0.1", + "::1" + ], + "name": "123", + "id": "123", + "mac": [ + "aa:aa:aa:aa:aa:aa" + ], + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "sequence": 16194584, + "ingested": "2024-01-26T15:06:52Z", + "created": "2024-01-26T15:06:50.254822749Z", + "kind": "metric", + "module": "endpoint", + "action": "endpoint_metrics", + "id": "123", + "category": [ + "host" + ], + "type": [ + "info" + ], + "dataset": "endpoint.metrics" + }, + "message": "Endpoint metrics" + }, + "type": "_doc" + } +} + +{ + "type": "doc", + "value": { + "index": "metrics-endpoint.metrics-01", + "source": { + "@timestamp": "2025-01-28T16:45:28.82746649Z", + "Endpoint": { + "metrics": { + "cloud_services": { + "reputation_lookups": {} + }, + "cpu": { + "endpoint": { + "histogram": { + "counts": [ + 6, + 3, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "values": [ + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 85, + 90, + 95, + 100 + ] + }, + "latest": 0.9750000000000001, + "mean": 4.843181818181818 + } + }, + "disks": [ + { + "device": "sysfs", + "free": 0, + "fstype": "sysfs", + "mount": "/sys", + "total": 0 + }, + { + "device": "proc", + "free": 0, + "fstype": "proc", + "mount": "/proc", + "total": 0 + }, + { + "device": "udev", + "free": 8307535872, + "fstype": "devtmpfs", + "mount": "/dev", + "total": 8307535872 + }, + { + "device": "devpts", + "free": 0, + "fstype": "devpts", + "mount": "/dev/pts", + "total": 0 + }, + { + "device": "tmpfs", + "free": 1670688768, + "fstype": "tmpfs", + "mount": "/run", + "total": 1672884224 + }, + { + "device": "/dev/mapper/ubuntu--vg-ubuntu--lv", + "endpoint_drive": true, + "free": 108122083328, + "fstype": "ext4", + "mount": "/", + "total": 208661176320 + }, + { + "device": "securityfs", + "free": 0, + "fstype": "securityfs", + "mount": "/sys/kernel/security", + "total": 0 + }, + { + "device": "tmpfs", + "free": 8364396544, + "fstype": "tmpfs", + "mount": "/dev/shm", + "total": 8364417024 + }, + { + "device": "tmpfs", + "free": 5242880, + "fstype": "tmpfs", + "mount": "/run/lock", + "total": 5242880 + }, + { + "device": "cgroup2", + "free": 0, + "fstype": "cgroup2", + "mount": "/sys/fs/cgroup", + "total": 0 + }, + { + "device": "pstore", + "free": 0, + "fstype": "pstore", + "mount": "/sys/fs/pstore", + "total": 0 + }, + { + "device": "bpf", + "free": 0, + "fstype": "bpf", + "mount": "/sys/fs/bpf", + "total": 0 + }, + { + "device": "systemd-1", + "free": 0, + "fstype": "autofs", + "mount": "/proc/sys/fs/binfmt_misc", + "total": 0 + }, + { + "device": "hugetlbfs", + "free": 0, + "fstype": "hugetlbfs", + "mount": "/dev/hugepages", + "total": 0 + }, + { + "device": "mqueue", + "free": 0, + "fstype": "mqueue", + "mount": "/dev/mqueue", + "total": 0 + }, + { + "device": "debugfs", + "free": 0, + "fstype": "debugfs", + "mount": "/sys/kernel/debug", + "total": 0 + }, + { + "device": "tracefs", + "free": 0, + "fstype": "tracefs", + "mount": "/sys/kernel/tracing", + "total": 0 + }, + { + "device": "fusectl", + "free": 0, + "fstype": "fusectl", + "mount": "/sys/fs/fuse/connections", + "total": 0 + }, + { + "device": "configfs", + "free": 0, + "fstype": "configfs", + "mount": "/sys/kernel/config", + "total": 0 + }, + { + "device": "none", + "free": 0, + "fstype": "ramfs", + "mount": "/run/credentials/systemd-sysusers.service", + "total": 0 + }, + { + "device": "/dev/loop1", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/core20/2434", + "total": 66846720 + }, + { + "device": "/dev/loop0", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/core20/2379", + "total": 67108864 + }, + { + "device": "/dev/loop3", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/core22/1722", + "total": 77463552 + }, + { + "device": "/dev/loop4", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/go/10748", + "total": 68812800 + }, + { + "device": "/dev/loop5", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/go/10818", + "total": 68812800 + }, + { + "device": "/dev/loop6", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/lxd/29351", + "total": 91357184 + }, + { + "device": "/dev/loop7", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/lxd/31333", + "total": 93847552 + }, + { + "device": "/dev/loop9", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/snapd/23545", + "total": 46661632 + }, + { + "device": "/dev/loop8", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/snapd/23258", + "total": 46530560 + }, + { + "device": "/dev/sda2", + "free": 1786802176, + "fstype": "ext4", + "mount": "/boot", + "total": 2040373248 + }, + { + "device": "binfmt_misc", + "free": 0, + "fstype": "binfmt_misc", + "mount": "/proc/sys/fs/binfmt_misc", + "total": 0 + }, + { + "device": "//192.168.1.186/Data", + "free": 7032009764864, + "fstype": "cifs", + "mount": "/mnt/nas", + "total": 23018619527168 + }, + { + "device": "tmpfs", + "free": 1670688768, + "fstype": "tmpfs", + "mount": "/run/snapd/ns", + "total": 1672884224 + }, + { + "device": "nsfs", + "free": 0, + "fstype": "nsfs", + "mount": "/run/snapd/ns/lxd.mnt", + "total": 0 + }, + { + "device": "overlay", + "free": 108122083328, + "fstype": "overlay", + "mount": "/var/lib/docker/overlay2/2efcbcf71b2b8e2af40ac3edfe79949164f83ce1c2ffaf54fb2ba6c8e7832982/merged", + "total": 208661176320 + }, + { + "device": "nsfs", + "free": 0, + "fstype": "nsfs", + "mount": "/run/docker/netns/c0f146abc951", + "total": 0 + }, + { + "device": "tracefs", + "free": 0, + "fstype": "tracefs", + "mount": "/sys/kernel/debug/tracing", + "total": 0 + }, + { + "device": "tmpfs", + "free": 1672876032, + "fstype": "tmpfs", + "mount": "/run/user/1000", + "total": 1672880128 + }, + { + "device": "/dev/loop10", + "free": 0, + "fstype": "squashfs", + "mount": "/snap/core22/1748", + "total": 77594624 + } + ], + "documents_volume": { + "file_events": { + "sent_bytes": 119257, + "sent_count": 94, + "suppressed_bytes": 0, + "suppressed_count": 0 + }, + "network_events": { + "sent_bytes": 4907, + "sent_count": 3, + "suppressed_bytes": 0, + "suppressed_count": 0 + }, + "overall": { + "sent_bytes": 2074039, + "sent_count": 1200, + "suppressed_bytes": 0, + "suppressed_count": 0 + }, + "process_events": { + "sent_bytes": 1949875, + "sent_count": 1103, + "suppressed_bytes": 0, + "suppressed_count": 0 + } + }, + "event_filter": { + "active_global_count": 2, + "active_user_count": 0 + }, + "exception_list": {}, + "malicious_behavior_rules": [ + { + "endpoint_uptime_percent": 0.0, + "id": "c4539c79-9f55-4b36-b06f-8aff82563bca" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "92bb2a27-745b-4291-90a1-b7b654df1379" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b9935dcc-e885-4954-9999-3c016b990737" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "31da6564-b3d3-4fc8-9a96-75ad0b364363" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "539488cc-baf9-465f-9700-f978b93b3f62" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "feed7842-34a6-4764-b858-6e5ac01a5ab7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "aec74eb4-9618-42ff-96eb-2d13e6959d47" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "95718a3c-edc7-46ef-978b-77891ca6198f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "13fd98ce-f1c3-423f-9441-45c50eb462c0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b3bcbab6-e216-4d70-bdee-2b69affbb386" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "5d7328aa-973b-41e7-a6b3-6f40ea3094f1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1ab34b34-4cfd-450d-befc-c3503095eaed" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "15019d7c-42e6-4cf7-88b0-0c3a6963e6f5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "d0e45f6c-1f83-4d97-a8d9-c8f9eb61c15c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "33309858-3154-47a6-b601-eda2de62557b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b7974ff6-82ff-4743-9e07-1c6901b1f0ea" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "133102a7-f906-4725-b382-09257a0209c2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3f3e9299-1a05-4922-aef2-6d855a07f8ef" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "52001df2-a3bf-411d-a09c-5f36a9f976b8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3cce374b-c877-4419-8230-08f1a6b04da2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "25ae94f5-0214-4bf1-b534-33d4ffc3d41c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b9b3922a-59ee-407c-8773-31b98bf9b18d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "995c8bdb-5ebb-4c5b-9a03-4d39b52c0ff3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "36a8d397-7fef-4bdf-9152-71c750168580" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "d4867fb6-183c-49f2-afbb-fd06144f9b11" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cd0844ea-6112-453f-a836-cc021a2b6afb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "879c083c-e2d9-4f75-84f2-0f1471d915a8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8012a863-8c0c-4461-bb88-b0193dfb9f38" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "52deef30-e633-49e1-9dd2-da1ad6cb5e43" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "18cdd8c1-4dc8-4ac1-8f7c-830be4c493cc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "22145fc0-dc4c-4187-8397-4d20162fc391" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "6929ab87-7b2f-4ef8-858a-1f8f1c239cac" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "7032dd32-8a51-4545-94d0-5997051f4610" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8fff17c6-f0ba-4996-bcc3-342a9ebd0ef3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "94f548c8-cad2-4bc5-bf61-c0e42558ef65" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "4c8b9c6f-4d85-4fa9-9104-16f7a99aded6" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c0ca8114-254d-46ba-88c6-db57de6efe2d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "52206861-4570-4b8b-a73e-4ef0ea379a4c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "947b70bb-8e01-4f1b-994d-5af9488556bb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0fd5b434-606b-42fc-bed0-87071293533e" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "46b142a6-3d54-45e7-ad8a-7a4bc9bfe01c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cb351778-7329-4de9-82b5-6705f772a3af" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b15d8277-ccd5-481b-82f8-c1681c5aada8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a67ef648-f04e-475c-8f53-a2db038ee834" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3bc8af69-707a-482c-b3c1-06bdb1530b94" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "d20cd4ba-ff65-4e1c-8012-4241d449b16b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "dc1cee03-4923-4c6b-b00b-8a5c323bb753" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fa71e336-0177-4732-b58b-53eb4a87a286" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b25ec4e7-34f1-40c2-b683-bbf1dcdd84e5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "268ffea4-fc13-4ab5-a473-07d10255ea8d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e6669bc3-cb75-4fb3-91e0-ddaa06dd59b2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "42c2e98b-b757-423f-ac25-8183d8c76b97" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "ad89ce4d-96df-4cc8-bfc5-979a73469b54" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "bb330560-0042-48a5-8232-7f2012d6e440" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3e79f8fc-26a4-4646-9a58-9f11b9c11e28" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "30f3021b-032e-4aa0-bc6f-341d0c82fdf5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e8d8eb78-d6aa-456f-a24e-c073026f7809" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0b206183-7f90-461d-80b3-8a147147ae78" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fdb6fde5-3af3-4759-a4cc-2c6847b09565" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e8b2afe5-37a9-468c-a6fb-f178d46cb698" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f2d206e0-97c9-484b-8b6a-5eecd82fbfdc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fcc42a61-4507-4918-867b-d673e5b065dc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "dd914805-e99b-4ff6-b445-775c53d44e10" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "112ed0c3-9685-4786-ad79-8307a2d426bf" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "272cf3e7-fd3f-442b-a781-f9e864fb1d4c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0c63849b-2e23-4720-9608-0a402d093d3c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e0db3577-879e-4ac2-bd58-691e1343afca" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fbf9342e-3d1e-4fba-a828-92fa0fb4d21b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "bfff8d1b-c4d7-4005-9f49-f494261e5a25" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a18e57c9-5627-4535-b994-64febc67c1e8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "18d82674-08d0-408e-801b-468e1b06298f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e8f9661a-a418-45ea-91cc-e2fa705e2ade" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b86c5998-3068-43e8-bfb5-ecb593e34ca9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "37f6659f-dff4-42bc-91ae-7ed7a9264529" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "10cb6563-35a9-45b7-a394-e7bca6fd5bed" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3337a10c-e950-4827-a44e-96a688fba221" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0fb019ab-98a0-487e-aa37-fabe5b69ec8a" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "4342c282-ee21-4140-8e27-4e0f551489ef" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "dbbd7fb0-8b29-4c96-901d-166dff728a3b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "34c0ed64-4456-437e-9bd2-92e6fed9bfb2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "aa02591f-c9e6-4317-841e-0b075b9515ff" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "508226f9-4030-4e86-86cd-63321b7164bc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e40b5e63-b737-49a3-9e38-1d8aef72c9e7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "47e5595e-1920-4fdd-9a1c-cf712e1112d1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "7d39db57-5c5e-4542-a9eb-8f5de524b09c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1471cf36-7e5c-47cc-bf39-2234df0e676a" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "eb928496-a992-4cee-9cd7-fc3fbae7e8da" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "50f67a77-d499-4f1f-8dfc-c47616b47a71" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "00a97e57-4834-47ec-880d-48ce3783e0b4" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "9015e5ec-a68d-4539-923d-a96d2c6227d3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3ee903d9-6839-48d6-9437-3823b77bbeaa" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fbc224a0-5469-4c0c-953d-fc57e0293197" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "2f4275f8-b305-455d-9f1f-c67574cc6b38" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cc8a82b6-eb6e-4e35-8c9e-e6ec3339e12d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b8bb0b6a-eb7e-4819-9c7e-4e3845b82b91" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "21692d53-d4a5-462c-9ee6-2d8788411996" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b1d81dfe-93d7-4d7d-827d-5def574e8cda" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "895ed985-a6ae-4ebe-b688-7ca8cd6e2e23" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e9731cea-c3fc-4183-a76c-9a798ae0a2b0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f6f74bd6-414a-4f22-89ec-c045d634a805" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b6585a25-db0e-4911-b860-3f117b1db60f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "831513aa-8320-484f-9275-5b46c57760f0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "2588a595-c6c7-4d8d-b287-57b9d1e3d7e6" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "18439a4c-cd67-43f5-ac42-5c4210edeacb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a13c8f01-36a5-4ad7-a282-8d297cf62860" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "742037b3-3ef6-4a33-84ed-b26fc6ae322c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fb2c3240-4cbe-413e-be78-5427807b618b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "05f95917-6942-4aab-a904-37c6db906503" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c71b9783-ca42-4532-8eb3-e8f2fe32ff39" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "30e19006-e1b4-4328-9553-b0284c5cec00" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "54873ebe-b8c8-4053-92b2-8ee8f57aabc1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e659b4b9-5bbf-4839-96b9-b489334b4ca1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "ea97874d-a232-44b4-a99f-be7850977cd7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "69b41dec-033a-4629-9b1d-dd2c54b507b9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cc29bf55-8d7f-45df-b8fe-212968c8951c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e5149069-189b-4b1a-ad24-9fed16f5a15b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "14f332f1-b20b-4628-b6de-14a4626fba79" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "6644d936-36a2-4d21-95f3-4826e6b61b9b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8c2977dd-07ce-4a8e-8ccd-5e4183138675" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "da02d81a-d432-4cfe-8aa4-fc1a31c29c98" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "436e12a8-7a03-4f6f-a3b2-3fe8b8f4c474" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0259c937-877f-4140-a67b-dc51298f3f86" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e3d9bd45-315f-47a6-8675-475e2d3f29ff" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "03d344f8-3a0c-4c2c-988f-cdba2aeadf0f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "08ad673a-7f99-417e-8b93-a79d4faeeed3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "08697d36-4c07-4f54-b177-a39e473705c0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "276a5df0-7e20-4218-ade1-3f3ed711d4cb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b38eb534-230c-45f4-93ba-fc516ac51630" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8ccebdc1-9929-4584-ac8f-a96ee8e8c616" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a0fce633-b6ee-4e4c-b6c7-ba46b8561e9e" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "87bee79f-cf0b-43a0-884a-a7a4ddbd4599" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "5b544dbb-2c66-42cd-a4ee-8d1e5afe9903" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b382c343-892d-46e1-8fad-22576a086598" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "74afd5bc-7d44-4a11-9383-a5e30c3ec8ae" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3144cab7-cc28-46c3-a3ac-8fefe8db22d6" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "47903010-d527-4200-b43d-971955a80924" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "38108046-32a6-407f-b5fd-6943ffdcdab0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "9aff7450-c93c-4b97-8c53-48392d798deb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "03195b53-de40-4a18-b727-6fb7ac3f94b7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "789f8a41-00cb-40cb-b41f-c2e1611b1245" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a12904d1-c9d5-4fa5-8727-38e64c81553e" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3002b96e-adaa-4660-b906-e021c0a1c086" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c932c9f0-76ed-4d78-a242-cfaade43080c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "40bec6eb-e93b-4d09-8265-66bba186e332" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "04ec0ec4-86c4-47e3-8c7b-8dad5f97532c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "55275da8-4d24-4844-b62e-5eadb7ff01b1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "2d0e4305-c7b8-46af-8473-775a49c18ec3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "30c89cc9-d93c-4134-a976-58f8413f2f32" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f0bc87ef-4acd-4dda-aff2-c13d80939e66" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cd6e64ec-2890-4bd8-9d07-bef06465b06f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "79165167-8419-4755-80c7-b151e08eee37" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1a2596ff-a5e7-4562-af17-97dbaf9284d5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "ca9de348-a09d-4c67-af21-5645b70003d0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "57ed0e43-643a-47f3-936e-138dc6f480da" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "656f671c-6b21-4aed-8a13-4d492f97273b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1e5af062-4973-42d1-94bb-0c1ecbd8daa4" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "613da93c-226e-4150-9125-3b476103c0b9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "78ae5dbd-477b-4ce7-a7f7-8c4b5e228df2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "7c4d6361-3e7f-481a-9313-d1d1c0e5a3a9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "4c61fca2-6f77-474d-a537-2d7fd9ec75e0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f2a52d42-2410-468b-9910-26823c6ef822" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "93d7b72d-3914-44fb-92bf-63675769ef12" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "332a8018-6615-4d2d-aeff-61e85e5ae77b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c14705f7-ebd3-4cf7-88b3-6bff2d832f1b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fa59e598-8adc-4798-af82-9f878934d975" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "21984abc-9efa-4de1-b1ad-bf797a7acec9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "ba70be59-bf50-48a9-8b36-0f0808a50fb8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c8d440b8-fb37-4903-814b-84a05d65f201" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "eb66a097-a2e0-4fc9-b1e8-c59d26fd9f93" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c52891b5-8f83-4571-8e68-ea2601f46285" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "eb575588-dce7-4934-966c-c0e9ebc456c5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "94943f02-5580-4d1d-a763-09e958bd0f57" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8704a5cf-f1e1-4de8-b102-634f237f2514" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "49ab63a4-ed52-49ac-855b-c298b38b0cb9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "d50a66ea-2065-45cc-a66f-0f7222e4c52d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a67ef648-f04e-475c-8f53-a2db038ee834" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "10cb6563-35a9-45b7-a394-e7bca6fd5bed" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cb351778-7329-4de9-82b5-6705f772a3af" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "30f3021b-032e-4aa0-bc6f-341d0c82fdf5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "ca9de348-a09d-4c67-af21-5645b70003d0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b86c5998-3068-43e8-bfb5-ecb593e34ca9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8fff17c6-f0ba-4996-bcc3-342a9ebd0ef3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e659b4b9-5bbf-4839-96b9-b489334b4ca1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "22145fc0-dc4c-4187-8397-4d20162fc391" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "6644d936-36a2-4d21-95f3-4826e6b61b9b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "276a5df0-7e20-4218-ade1-3f3ed711d4cb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fdb6fde5-3af3-4759-a4cc-2c6847b09565" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "21692d53-d4a5-462c-9ee6-2d8788411996" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "52206861-4570-4b8b-a73e-4ef0ea379a4c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "d20cd4ba-ff65-4e1c-8012-4241d449b16b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cc29bf55-8d7f-45df-b8fe-212968c8951c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "94943f02-5580-4d1d-a763-09e958bd0f57" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "eb928496-a992-4cee-9cd7-fc3fbae7e8da" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "52deef30-e633-49e1-9dd2-da1ad6cb5e43" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "04ec0ec4-86c4-47e3-8c7b-8dad5f97532c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1e5af062-4973-42d1-94bb-0c1ecbd8daa4" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8c2977dd-07ce-4a8e-8ccd-5e4183138675" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a12904d1-c9d5-4fa5-8727-38e64c81553e" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "ba70be59-bf50-48a9-8b36-0f0808a50fb8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f0bc87ef-4acd-4dda-aff2-c13d80939e66" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "79165167-8419-4755-80c7-b151e08eee37" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "57ed0e43-643a-47f3-936e-138dc6f480da" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "742037b3-3ef6-4a33-84ed-b26fc6ae322c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "18439a4c-cd67-43f5-ac42-5c4210edeacb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "42c2e98b-b757-423f-ac25-8183d8c76b97" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "47903010-d527-4200-b43d-971955a80924" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3144cab7-cc28-46c3-a3ac-8fefe8db22d6" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "52001df2-a3bf-411d-a09c-5f36a9f976b8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "332a8018-6615-4d2d-aeff-61e85e5ae77b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c0ca8114-254d-46ba-88c6-db57de6efe2d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "dbbd7fb0-8b29-4c96-901d-166dff728a3b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1471cf36-7e5c-47cc-bf39-2234df0e676a" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c14705f7-ebd3-4cf7-88b3-6bff2d832f1b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "9aff7450-c93c-4b97-8c53-48392d798deb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b38eb534-230c-45f4-93ba-fc516ac51630" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "dd914805-e99b-4ff6-b445-775c53d44e10" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "78ae5dbd-477b-4ce7-a7f7-8c4b5e228df2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "14f332f1-b20b-4628-b6de-14a4626fba79" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a13c8f01-36a5-4ad7-a282-8d297cf62860" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "37f6659f-dff4-42bc-91ae-7ed7a9264529" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "4c8b9c6f-4d85-4fa9-9104-16f7a99aded6" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "30e19006-e1b4-4328-9553-b0284c5cec00" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cd6e64ec-2890-4bd8-9d07-bef06465b06f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "2d0e4305-c7b8-46af-8473-775a49c18ec3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "272cf3e7-fd3f-442b-a781-f9e864fb1d4c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b15d8277-ccd5-481b-82f8-c1681c5aada8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "bb330560-0042-48a5-8232-7f2012d6e440" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1a2596ff-a5e7-4562-af17-97dbaf9284d5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0fb019ab-98a0-487e-aa37-fabe5b69ec8a" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "cd0844ea-6112-453f-a836-cc021a2b6afb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a18e57c9-5627-4535-b994-64febc67c1e8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "aa02591f-c9e6-4317-841e-0b075b9515ff" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "613da93c-226e-4150-9125-3b476103c0b9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fbf9342e-3d1e-4fba-a828-92fa0fb4d21b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b9b3922a-59ee-407c-8773-31b98bf9b18d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "da02d81a-d432-4cfe-8aa4-fc1a31c29c98" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "38108046-32a6-407f-b5fd-6943ffdcdab0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "30c89cc9-d93c-4134-a976-58f8413f2f32" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fbc224a0-5469-4c0c-953d-fc57e0293197" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "21984abc-9efa-4de1-b1ad-bf797a7acec9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "995c8bdb-5ebb-4c5b-9a03-4d39b52c0ff3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c932c9f0-76ed-4d78-a242-cfaade43080c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "9015e5ec-a68d-4539-923d-a96d2c6227d3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "08ad673a-7f99-417e-8b93-a79d4faeeed3" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fb2c3240-4cbe-413e-be78-5427807b618b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "18d82674-08d0-408e-801b-468e1b06298f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "656f671c-6b21-4aed-8a13-4d492f97273b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f2d206e0-97c9-484b-8b6a-5eecd82fbfdc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "831513aa-8320-484f-9275-5b46c57760f0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3ee903d9-6839-48d6-9437-3823b77bbeaa" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "2588a595-c6c7-4d8d-b287-57b9d1e3d7e6" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fa71e336-0177-4732-b58b-53eb4a87a286" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "947b70bb-8e01-4f1b-994d-5af9488556bb" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c71b9783-ca42-4532-8eb3-e8f2fe32ff39" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e8f9661a-a418-45ea-91cc-e2fa705e2ade" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "46b142a6-3d54-45e7-ad8a-7a4bc9bfe01c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3337a10c-e950-4827-a44e-96a688fba221" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "7c4d6361-3e7f-481a-9313-d1d1c0e5a3a9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "895ed985-a6ae-4ebe-b688-7ca8cd6e2e23" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "5b544dbb-2c66-42cd-a4ee-8d1e5afe9903" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "08697d36-4c07-4f54-b177-a39e473705c0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "93d7b72d-3914-44fb-92bf-63675769ef12" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b382c343-892d-46e1-8fad-22576a086598" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "dc1cee03-4923-4c6b-b00b-8a5c323bb753" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "03195b53-de40-4a18-b727-6fb7ac3f94b7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "508226f9-4030-4e86-86cd-63321b7164bc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0c63849b-2e23-4720-9608-0a402d093d3c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e40b5e63-b737-49a3-9e38-1d8aef72c9e7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "112ed0c3-9685-4786-ad79-8307a2d426bf" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "0b206183-7f90-461d-80b3-8a147147ae78" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b1d81dfe-93d7-4d7d-827d-5def574e8cda" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3e79f8fc-26a4-4646-9a58-9f11b9c11e28" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "74afd5bc-7d44-4a11-9383-a5e30c3ec8ae" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f6f74bd6-414a-4f22-89ec-c045d634a805" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3bc8af69-707a-482c-b3c1-06bdb1530b94" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "c52891b5-8f83-4571-8e68-ea2601f46285" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "268ffea4-fc13-4ab5-a473-07d10255ea8d" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "bfff8d1b-c4d7-4005-9f49-f494261e5a25" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "05f95917-6942-4aab-a904-37c6db906503" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e8b2afe5-37a9-468c-a6fb-f178d46cb698" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "4342c282-ee21-4140-8e27-4e0f551489ef" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3cce374b-c877-4419-8230-08f1a6b04da2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "436e12a8-7a03-4f6f-a3b2-3fe8b8f4c474" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "a0fce633-b6ee-4e4c-b6c7-ba46b8561e9e" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "8ccebdc1-9929-4584-ac8f-a96ee8e8c616" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "7032dd32-8a51-4545-94d0-5997051f4610" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "47e5595e-1920-4fdd-9a1c-cf712e1112d1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "2f4275f8-b305-455d-9f1f-c67574cc6b38" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "87bee79f-cf0b-43a0-884a-a7a4ddbd4599" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e3d9bd45-315f-47a6-8675-475e2d3f29ff" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e6669bc3-cb75-4fb3-91e0-ddaa06dd59b2" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "54873ebe-b8c8-4053-92b2-8ee8f57aabc1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fa59e598-8adc-4798-af82-9f878934d975" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "789f8a41-00cb-40cb-b41f-c2e1611b1245" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "00a97e57-4834-47ec-880d-48ce3783e0b4" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "3f3e9299-1a05-4922-aef2-6d855a07f8ef" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "4c61fca2-6f77-474d-a537-2d7fd9ec75e0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b25ec4e7-34f1-40c2-b683-bbf1dcdd84e5" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "fcc42a61-4507-4918-867b-d673e5b065dc" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "e9731cea-c3fc-4183-a76c-9a798ae0a2b0" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "94f548c8-cad2-4bc5-bf61-c0e42558ef65" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "f2a52d42-2410-468b-9910-26823c6ef822" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "879c083c-e2d9-4f75-84f2-0f1471d915a8" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "69b41dec-033a-4629-9b1d-dd2c54b507b9" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "03d344f8-3a0c-4c2c-988f-cdba2aeadf0f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "55275da8-4d24-4844-b62e-5eadb7ff01b1" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "1ab34b34-4cfd-450d-befc-c3503095eaed" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "31da6564-b3d3-4fc8-9a96-75ad0b364363" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b3bcbab6-e216-4d70-bdee-2b69affbb386" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "feed7842-34a6-4764-b858-6e5ac01a5ab7" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "95718a3c-edc7-46ef-978b-77891ca6198f" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b7974ff6-82ff-4743-9e07-1c6901b1f0ea" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "b9935dcc-e885-4954-9999-3c016b990737" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "d0e45f6c-1f83-4d97-a8d9-c8f9eb61c15c" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "92bb2a27-745b-4291-90a1-b7b654df1379" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "33309858-3154-47a6-b601-eda2de62557b" + }, + { + "endpoint_uptime_percent": 0.0, + "id": "15019d7c-42e6-4cf7-88b0-0c3a6963e6f5" + } + ], + "memory": { + "endpoint": { + "private": { + "latest": 184266752, + "mean": 144539989 + } + } + }, + "system_impact": [ + { + "behavior_protection": { + "week_idle_ms": 0, + "week_ms": 4016 + }, + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 4370 + }, + "memory_scan": { + "week_idle_ms": 0, + "week_ms": 3 + }, + "overall": { + "week_idle_ms": 717064, + "week_ms": 12742 + }, + "process": { + "executable": "/opt/miniconda3/envs/Endpoint/bin/python" + }, + "process_events": { + "week_idle_ms": 717064, + "week_ms": 4353 + } + }, + { + "behavior_protection": { + "week_idle_ms": 0, + "week_ms": 349 + }, + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 397 + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 1185 + }, + "overall": { + "week_idle_ms": 11882, + "week_ms": 2696 + }, + "process": { + "executable": "/usr/bin/sudo" + }, + "process_events": { + "week_idle_ms": 11882, + "week_ms": 765 + } + }, + { + "behavior_protection": { + "week_idle_ms": 0, + "week_ms": 210 + }, + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 203 + }, + "file_events": { + "week_idle_ms": 1321, + "week_ms": 32 + }, + "malware": { + "week_idle_ms": 0, + "week_ms": 1927 + }, + "network_events": { + "week_idle_ms": 117, + "week_ms": 8 + }, + "overall": { + "week_idle_ms": 10201, + "week_ms": 2464 + }, + "process": { + "executable": "/opt/miniconda3/envs/Endpoint/bin/python3.10" + }, + "process_events": { + "week_idle_ms": 8763, + "week_ms": 84 + } + }, + { + "network_events": { + "week_idle_ms": 1381, + "week_ms": 8 + }, + "overall": { + "week_idle_ms": 31481, + "week_ms": 577 + }, + "process": { + "executable": "/home/brian/.vscode-server/cli/servers/Stable-384ff7382de624fb94dbaf6da11977bba1ecd427/server/node" + }, + "process_events": { + "week_idle_ms": 30100, + "week_ms": 569 + } + }, + { + "overall": { + "week_idle_ms": 4957, + "week_ms": 313 + }, + "process": { + "executable": "/snap/snapd/23545/usr/lib/snapd/snapd" + }, + "process_events": { + "week_idle_ms": 4957, + "week_ms": 313 + } + }, + { + "overall": { + "week_idle_ms": 4510, + "week_ms": 240 + }, + "process": { + "executable": "/var/lib/docker/overlay2/2efcbcf71b2b8e2af40ac3edfe79949164f83ce1c2ffaf54fb2ba6c8e7832982/merged/usr/local/bin/influxd" + }, + "process_events": { + "week_idle_ms": 4510, + "week_ms": 240 + } + }, + { + "overall": { + "week_idle_ms": 183217, + "week_ms": 219 + }, + "process": { + "executable": "/usr/bin/bash" + }, + "process_events": { + "week_idle_ms": 183217, + "week_ms": 219 + } + }, + { + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 1 + }, + "file_events": { + "week_idle_ms": 66834, + "week_ms": 190 + }, + "overall": { + "week_idle_ms": 75131, + "week_ms": 210 + }, + "process": { + "executable": "/tmp/eaf_elasticsearch-8.15.2-linux-x86_64/elasticsearch-8.15.2/jdk/bin/java" + }, + "process_events": { + "week_idle_ms": 8297, + "week_ms": 19 + } + }, + { + "overall": { + "week_idle_ms": 4745, + "week_ms": 192 + }, + "process": { + "executable": "/usr/bin/dockerd" + }, + "process_events": { + "week_idle_ms": 4745, + "week_ms": 192 + } + }, + { + "behavior_protection": { + "week_idle_ms": 0, + "week_ms": 41 + }, + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 44 + }, + "overall": { + "week_idle_ms": 4423, + "week_ms": 185 + }, + "process": { + "executable": "/opt/Elastic/Endpoint/elastic-endpoint" + }, + "process_events": { + "week_idle_ms": 4423, + "week_ms": 100 + } + }, + { + "overall": { + "week_idle_ms": 4211, + "week_ms": 126 + }, + "process": { + "executable": "/home/brian/.vscode-server/extensions/ms-vscode.cpptools-1.22.11-linux-x64/bin/cpptools" + }, + "process_events": { + "week_idle_ms": 4211, + "week_ms": 126 + } + }, + { + "overall": { + "week_idle_ms": 4865, + "week_ms": 102 + }, + "process": { + "executable": "/usr/bin/containerd" + }, + "process_events": { + "week_idle_ms": 4865, + "week_ms": 102 + } + }, + { + "overall": { + "week_idle_ms": 4081, + "week_ms": 85 + }, + "process": { + "executable": "/home/brian/.vscode-server/extensions/ms-vscode.cpptools-1.22.11-linux-x64/bin/cpptools-srv" + }, + "process_events": { + "week_idle_ms": 4081, + "week_ms": 85 + } + }, + { + "overall": { + "week_idle_ms": 4044, + "week_ms": 80 + }, + "process": { + "executable": "/home/brian/.vscode-server/code-384ff7382de624fb94dbaf6da11977bba1ecd427" + }, + "process_events": { + "week_idle_ms": 4044, + "week_ms": 80 + } + }, + { + "overall": { + "week_idle_ms": 10115, + "week_ms": 30 + }, + "process": { + "executable": "/usr/bin/python3.10" + }, + "process_events": { + "week_idle_ms": 10115, + "week_ms": 30 + } + }, + { + "overall": { + "week_idle_ms": 21189, + "week_ms": 30 + }, + "process": { + "executable": "/usr/sbin/sshd" + }, + "process_events": { + "week_idle_ms": 21189, + "week_ms": 30 + } + }, + { + "behavior_protection": { + "week_idle_ms": 0, + "week_ms": 9 + }, + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 9 + }, + "overall": { + "week_idle_ms": 48, + "week_ms": 29 + }, + "process": { + "executable": "/usr/bin/stat" + }, + "process_events": { + "week_idle_ms": 48, + "week_ms": 11 + } + }, + { + "behavior_protection": { + "week_idle_ms": 0, + "week_ms": 9 + }, + "diagnostic_behavior_protection": { + "week_idle_ms": 0, + "week_ms": 9 + }, + "overall": { + "week_idle_ms": 56, + "week_ms": 28 + }, + "process": { + "executable": "/usr/bin/chmod" + }, + "process_events": { + "week_idle_ms": 56, + "week_ms": 10 + } + }, + { + "overall": { + "week_idle_ms": 12626, + "week_ms": 27 + }, + "process": { + "executable": "/usr/lib/systemd/systemd" + }, + "process_events": { + "week_idle_ms": 12626, + "week_ms": 27 + } + }, + { + "overall": { + "week_idle_ms": 4740, + "week_ms": 24 + }, + "process": { + "executable": "/usr/bin/containerd-shim-runc-v2" + }, + "process_events": { + "week_idle_ms": 4740, + "week_ms": 24 + } + } + ], + "threads": [ + { + "cpu": { + "mean": 0.0 + }, + "name": "Cron" + }, + { + "cpu": { + "mean": 1.612903225806452 + }, + "name": "FileLogThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "LoggingLimitThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "DocumentLoggingThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "DocumentLoggingMaintenance" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "BulkConsumerThread" + }, + { + "cpu": { + "mean": 1.612903225806452 + }, + "name": "DocumentLoggingConsumerThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "DocumentLoggingLimitThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "ArtifactManifestDownload" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "PolicyReloadThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "PerformanceMonitorWorkerThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "MetadataThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "MountMonitor" + }, + { + "cpu": { + "mean": 14.75409836065574 + }, + "name": "EventsQueueThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "DelayedAlertEnrichment" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "MaintainProcessMap" + }, + { + "cpu": { + "mean": 1.639344262295082 + }, + "name": "FileScoreThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "DiagnosticMalwareThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "QuarantineManagerWorkerThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "EventProcessingThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "HostIsolationMonitorThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "AsyncReputationLookupsThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "ReputationSamplesUploadThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "ImpactMonitorThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "serviceCommsThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "grpcConnectionManagerThread" + }, + { + "cpu": { + "mean": 18.0327868852459 + }, + "name": "checkinAPIThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "actionsAPIThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "stateReportThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "EventsLoopThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "FanotifyWatchdog" + }, + { + "cpu": { + "mean": 2.564102564102564 + }, + "name": "FanotifySyncConsumer" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "FanotifyAsyncConsumer" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "FanotifyConsumer" + }, + { + "cpu": { + "mean": 35.29411764705883 + }, + "name": "RulesEngineThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "responseActionsGetFileUploadThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "responseActionsGetFileProcessThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "responseActionsExecuteUploadThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "responseActionsExecuteProcessThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "responseActionsPutFileProcessThread" + }, + { + "cpu": { + "mean": 0.0 + }, + "name": "responseActionsScanThread" + }, + { + "name": "ServiceCommsDispatchInstance" + } + ], + "top_process_trees": { + "values": [ + { + "event_count": 1726, + "last_seen": "2025-01-28T16:43:49.0Z", + "sample": { + "command_line": "/opt/miniconda3/envs/Endpoint/bin/python /opt/miniconda3/envs/Endpoint/bin/pytest -s -v --show-capture=no -k test_noisy_processes_validate_metadata", + "entity_id": "CaOqGgCnYo6Wxqe5CYLRBQ", + "executable": "python3.10", + "parent_command_line": "-bash" + } + }, + { + "event_count": 1502, + "last_seen": "2025-01-28T16:45:02.93Z", + "sample": { + "command_line": "/opt/miniconda3/envs/Endpoint/bin/python /git/endpoint-dev/Python/endpoint/tools/noisy_process_tree_test.py --child --marker e24383f3cc6c804d9be34aa9982f7ca4", + "entity_id": "2xZDJ2oZ/6qK8UALFbKUAw", + "executable": "python", + "parent_command_line": "/opt/miniconda3/envs/Endpoint/bin/python /git/endpoint-dev/Python/endpoint/tools/noisy_process_tree_test.py --marker e24383f3cc6c804d9be34aa9982f7ca4 --count 2000" + } + }, + { + "event_count": 100, + "last_seen": "2025-01-28T16:45:01.37Z", + "sample": { + "command_line": "sudo /opt/Elastic/Endpoint/elastic-endpoint inspect", + "entity_id": "Ni/hCgF773ZtGv1li87hrw", + "executable": "sudo", + "parent_command_line": "/opt/miniconda3/envs/Endpoint/bin/python /opt/miniconda3/envs/Endpoint/bin/pytest -s -v --show-capture=no -k test_noisy_processes_validate_metadata" + } + } + ], + "window_end": "2025-01-28T16:45:28.8381494Z", + "window_start": "2025-01-28T04:45:28.8381494Z" + }, + "uptime": { + "endpoint": 62, + "system": 235881 + } + } + }, + "agent": { + "build": { + "original": "version: 9.0.0-SNAPSHOT, compiled: Tue Jan 28 16:00:00 2025, branch: 14632_noisy_process_tests, commit: fbadbb40da3484daf848142ed7cc3f18cffb750e" + }, + "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + "type": "endpoint", + "version": "9.0.0-SNAPSHOT" + }, + "data_stream": { + "dataset": "endpoint.metrics", + "namespace": "default", + "type": "metrics" + }, + "ecs": { + "version": "8.10.0" + }, + "elastic": { + "agent": { + "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" + } + }, + "event": { + "action": "endpoint_metrics", + "category": [ + "host" + ], + "created": "2025-01-28T16:45:28.82746649Z", + "dataset": "endpoint.metrics", + "id": "NtY7TLHCjYuLSjUP+++++/O/", + "kind": "metric", + "module": "endpoint", + "sequence": 3742, + "type": [ + "info" + ] + }, + "host": { + "architecture": "x86_64", + "hostname": "ubuntu-server", + "id": "dabadaba-0000-0000-0000-000000000000", + "ip": [ + "172.20.0.1", + "172.24.0.1", + "172.22.0.1", + "172.19.0.1", + "172.18.0.1", + "127.0.0.1", + "::1", + "192.168.224.128", + "fe80::20c:29ff:fe27:b344", + "192.168.177.100", + "fe80::20c:29ff:fe27:b34e", + "172.21.0.1", + "fe80::42:18ff:fe3f:e64", + "172.17.0.1", + "fe80::42:35ff:fe21:4364", + "fe80::9890:baff:fe17:1cf" + ], + "mac": [ + "02-42-f1-c9-0b-12", + "02-42-25-77-34-be", + "02-42-91-e1-74-ae", + "02-42-92-43-9e-26", + "02-42-a0-45-dd-ea", + "00-0c-29-27-b3-44", + "00-0c-29-27-b3-4e", + "02-42-18-3f-0e-64", + "02-42-35-21-43-64", + "9a-90-ba-17-01-cf" + ], + "name": "ubuntu-server", + "os": { + "Ext": { + "variant": "Ubuntu" + }, + "family": "ubuntu", + "full": "Ubuntu 22.04.5", + "kernel": "5.15.0-130-generic #140-Ubuntu SMP Wed Dec 18 17:59:53 UTC 2024", + "name": "Linux", + "platform": "ubuntu", + "type": "linux", + "version": "22.04.5" + } + }, + "message": "Endpoint metrics" + }, + "type": "_doc" + } +} diff --git a/x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics/mappings.json b/x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics/mappings.json new file mode 100644 index 0000000000000..23dc757690f8e --- /dev/null +++ b/x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics/mappings.json @@ -0,0 +1,69 @@ +{ + "type": "data_stream", + "value": { + "data_stream": "metrics-endpoint.metrics-01", + "template": { + "_meta": { + "managed": true, + "managed_by": "fleet", + "package": { + "name": "endpoint" + } + }, + "data_stream": { + "allow_custom_routing": false, + "hidden": false + }, + "ignore_missing_component_templates": [], + "index_patterns": [ + "metrics-endpoint.metrics-*" + ], + "name": "metrics-endpoint.metrics", + "priority": 200, + "template": { + "mappings": { + "_meta": { + "managed": true, + "managed_by": "fleet", + "package": { + "name": "endpoint" + } + }, + "date_detection": false, + "dynamic_templates": [], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "Endpoint": { + "properties": { + "metrics": { + "properties": { + "threads": { + "properties": { + "cpu": { + "properties": { + "mean": { + "type": "float" + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.kql_query_fields.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.kql_query_fields.ts index d00d2d842f2ba..50bdb83744f04 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.kql_query_fields.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/upgrade_review_prebuilt_rules.kql_query_fields.ts @@ -179,6 +179,110 @@ export default ({ getService }: FtrProviderContext): void => { expect(reviewResponse.stats.num_rules_with_non_solvable_conflicts).toBe(0); }); }); + + describe('when all query versions have filters with alias fields set to null', () => { + it('should not show in the upgrade/_review API response', async () => { + // Install base prebuilt detection rule + await createHistoricalPrebuiltRuleAssetSavedObjects(es, [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 1, + type: 'query', + query: 'query string = true', + language: 'kuery', + filters: [ + { + meta: { + negate: false, + disabled: false, + type: 'phrase', + key: 'test', + params: { + query: 'value', + }, + }, + query: { + term: { + field: 'value', + }, + }, + }, + ], + }), + ]); + await installPrebuiltRules(es, supertest); + + // Customize a kql_query field on the installed rule + await updateRule(supertest, { + ...getPrebuiltRuleMock(), + rule_id: 'rule-1', + type: 'query', + query: 'query string = true', + language: 'kuery', + filters: [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'test', + params: { + query: 'value', + }, + }, + query: { + term: { + field: 'value', + }, + }, + }, + ], + saved_id: undefined, + } as RuleUpdateProps); + + // Add a v2 rule asset to make the upgrade possible, do NOT update the related kql_query field, and create the new rule assets + const updatedRuleAssetSavedObjects = [ + createRuleAssetSavedObject({ + rule_id: 'rule-1', + version: 2, + type: 'query', + query: 'query string = true', + language: 'kuery', + filters: [ + { + meta: { + negate: false, + disabled: false, + type: 'phrase', + key: 'test', + params: { + query: 'value', + }, + }, + query: { + term: { + field: 'value', + }, + }, + }, + ], + }), + ]; + await createHistoricalPrebuiltRuleAssetSavedObjects(es, updatedRuleAssetSavedObjects); + + // Call the upgrade review prebuilt rules endpoint and check that there is 1 rule eligible for update but kql_query field is NOT returned + const reviewResponse = await reviewPrebuiltRulesToUpgrade(supertest); + const fieldDiffObject = reviewResponse.rules[0].diff.fields as AllFieldsDiff; + expect(fieldDiffObject.kql_query).toBeUndefined(); + + expect(reviewResponse.rules[0].diff.num_fields_with_updates).toBe(1); // `version` is considered an updated field + expect(reviewResponse.rules[0].diff.num_fields_with_conflicts).toBe(0); + expect(reviewResponse.rules[0].diff.num_fields_with_non_solvable_conflicts).toBe(0); + expect(reviewResponse.stats.num_rules_with_conflicts).toBe(0); + expect(reviewResponse.stats.num_rules_with_non_solvable_conflicts).toBe(0); + }); + }); }); describe("when rule field doesn't have an update but has a custom value - scenario ABA", () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts index 1f6f2c4e8bf83..126d398556bc9 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../../../../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext): void => { describe('Rules Management - Prebuilt Rules - Prebuilt Rule Customization Disabled', function () { + this.tags('skipFIPS'); loadTestFile(require.resolve('./is_customized_calculation')); loadTestFile(require.resolve('./upgrade_perform_prebuilt_rules')); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts index 7ee0ce440caad..2bf9656622749 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/perform_bulk_action_ess.ts @@ -51,7 +51,8 @@ export default ({ getService }: FtrProviderContext): void => { const createWebHookConnector = () => createConnector(getWebHookAction()); - describe('@ess perform_bulk_action - ESS specific logic', () => { + // 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/entity_store.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entity_store.ts index 5706dddef75e1..3bcf26fae835e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entity_store.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/entity_store/trial_license_complete_tier/entity_store.ts @@ -274,7 +274,8 @@ export default ({ getService }: FtrProviderContext) => { }); }); - describe('apply_dataview_indices', () => { + // FLAKY: https://github.com/elastic/kibana/issues/209010 + describe.skip('apply_dataview_indices', () => { before(async () => { await utils.initEntityEngineForEntityTypesAndWait(['host']); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/telemetry/index.ts b/x-pack/test/security_solution_api_integration/test_suites/telemetry/index.ts index ff88de12d7124..4619e1c374707 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/telemetry/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/telemetry/index.ts @@ -9,5 +9,6 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext): void => { describe('Security Solution - Telemetry', function () { loadTestFile(require.resolve('./tasks/indices_metadata')); + loadTestFile(require.resolve('./tasks/endpoint')); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/telemetry/tasks/endpoint.ts b/x-pack/test/security_solution_api_integration/test_suites/telemetry/tasks/endpoint.ts new file mode 100644 index 0000000000000..a8af3fb7101e0 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/telemetry/tasks/endpoint.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { getSecurityTelemetryStats } from '../../detections_response/utils'; + +export default ({ getService }: FtrProviderContext) => { + const logger = getService('log'); + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + describe('Endpoint metrics and info task.', function () { + describe('@ess @serverless Execution', () => { + this.tags('skipServerless'); + beforeEach(async () => { + await esArchiver + .load('x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics', { + useCreate: true, + }) + .catch((e) => { + logger.error('>> Endpoint metrics and info task: load'); + logger.error(e); + }); + + await es + .updateByQuery({ + index: '.ds-metrics-endpoint.metrics-*', + script: { + source: + 'ctx._source["@timestamp"] = Instant.ofEpochMilli(System.currentTimeMillis()).toString();', + }, + }) + .catch((e) => { + logger.error('>> Endpoint metrics and info task: update timestamps'); + logger.error(e); + }); + }); + + afterEach(async () => { + await esArchiver + .unload('x-pack/test/security_solution_api_integration/es_archive/endpoint/metrics') + .catch((e) => { + logger.error('>> Endpoint metrics and info task: unload'); + logger.error(e); + }); + }); + + it('should execute when scheduled', async () => { + const endpoints = await getSecurityTelemetryStats(supertest, logger).then((stats) => { + return stats.endpoints as any[]; + }); + expect(endpoints).to.not.be(undefined); + expect(endpoints).to.length(4); + }); + + it('should execute send mandatory fields', async () => { + const endpoints = await getSecurityTelemetryStats(supertest, logger).then((stats) => { + return stats.endpoints as any[]; + }); + expect(endpoints).to.not.be(undefined); + expect(endpoints).to.length(4); + const metrics = endpoints.flat().filter((endpoint) => { + return endpoint.endpoint_metrics !== undefined; + }); + expect(metrics).to.length(3); + for (const metric of metrics) { + expect(metric.endpoint_metrics.cpu).to.not.be(undefined); + expect(metric.endpoint_metrics.memory).to.not.be(undefined); + expect(metric.endpoint_metrics.uptime).to.not.be(undefined); + expect(metric.endpoint_metrics.documentsVolume).to.not.be(undefined); + expect(metric.endpoint_metrics.maliciousBehaviorRules).to.not.be(undefined); + expect(metric.endpoint_metrics.systemImpact).to.not.be(undefined); + expect(metric.endpoint_metrics.threads).to.not.be(undefined); + expect(metric.endpoint_metrics.eventFilter).to.not.be(undefined); + } + + const topProcessTrees = metrics + .filter((metric) => { + return metric.endpoint_metrics.topProcessTrees !== undefined; + }) + .map((metric) => { + return metric.endpoint_metrics.topProcessTrees; + }); + + expect(topProcessTrees).to.length(1); + + const topProcessTree = topProcessTrees[0]; + expect(topProcessTree.values).to.length(3); + + const event = topProcessTree.values[0]; + expect(event.event_count).to.be(1726); + expect(event.last_seen).to.be('2025-01-28T16:43:49.0Z'); + expect(event.sample.command_line).to.match(/.*python.*/); + expect(event.sample.entity_id).to.be('CaOqGgCnYo6Wxqe5CYLRBQ'); + expect(event.sample.executable).to.be('python3.10'); + expect(event.sample.parent_command_line).to.be('-bash'); + }); + }); + }); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/block_list.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/block_list.cy.ts index f90b962e1d57c..c8da8a49b873e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/block_list.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/block_list.cy.ts @@ -8,6 +8,7 @@ import { visitWithTimeRange } from '../../../tasks/navigation'; import { closeFlyout, + navigateToBlocklist, navigateToThreatIntelligence, openFlyout, openFlyoutTakeAction, @@ -19,7 +20,6 @@ import { openAddToBlockListFlyoutFromTable, openAddToBlocklistFromFlyout, } from '../../../tasks/threat_intelligence/blocklist'; -import { navigateToBlocklist } from '../../../tasks/threat_intelligence/common'; import { login } from '../../../tasks/login'; import { BLOCK_LIST_VALUE_INPUT, @@ -57,8 +57,7 @@ describe('Block list with invalid indicators', { tags: ['@ess'] }, () => { }); }); -// FAILING VERSION BUMP: https://github.com/elastic/kibana/issues/209051 -describe.skip('Block list interactions', { tags: ['@ess'] }, () => { +describe('Block list interactions', { tags: ['@ess'] }, () => { before(() => cy.task('esArchiverLoad', { archiveName: 'ti_indicators_data_multiple' })); after(() => cy.task('esArchiverUnload', { archiveName: 'ti_indicators_data_multiple' })); @@ -71,7 +70,7 @@ describe.skip('Block list interactions', { tags: ['@ess'] }, () => { it('should add to block list from the indicators table and from flyout', () => { // first indicator is a valid indicator for add to blocklist feature - const firstIndicatorId = 'd86e656455f985357df3063dff6637f7f3b95bb27d1769a6b88c7adecaf7763f'; + const firstIndicatorId = '7cbf47ef916aa02a1b39cad40dfe71ea121d8d5b36d5a13fdec5977a8dcb4550'; cy.log('add to blocklist from the table more action menu'); @@ -91,7 +90,7 @@ describe.skip('Block list interactions', { tags: ['@ess'] }, () => { navigateToThreatIntelligence(); // second indicator is a valid indicator for add to blocklist feature - const secondIndicatorId = 'd3e2cf87eabf84ef929aaf8dad1431b3387f5a26de8ffb7a0c3c2a13f973c0ab'; + const secondIndicatorId = 'd4ba36cfa7e4191199836b228f6d79bd74e86793bc183563b78591f508b066ed'; cy.log('add to blocklist from the flyout'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/cases.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/cases.cy.ts index 2c1fbbcb95cef..755703f48189e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/cases.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/cases.cy.ts @@ -33,8 +33,7 @@ import { login } from '../../../tasks/login'; const URL = '/app/security/threat_intelligence/indicators'; -// FAILING VERSION BUMP: https://github.com/elastic/kibana/issues/209050 -describe.skip('Cases with invalid indicators', { tags: ['@ess'] }, () => { +describe('Cases with invalid indicators', { tags: ['@ess'] }, () => { before(() => cy.task('esArchiverLoad', { archiveName: 'ti_indicators_data_invalid' })); after(() => cy.task('esArchiverUnload', { archiveName: 'ti_indicators_data_invalid' })); @@ -106,7 +105,7 @@ describe('Cases interactions', { tags: ['@ess'] }, () => { cy.log('should add to new case when clicking on the button in the indicators flyout'); - openFlyout(0); + openFlyout(); openFlyoutTakeAction(); openAddToNewCaseFromFlyout(); createNewCaseFromTI(); @@ -123,7 +122,7 @@ describe('Cases interactions', { tags: ['@ess'] }, () => { cy.log('should add to existing case when clicking on the button in the indicators flyout'); - openFlyout(0); + openFlyout(); openFlyoutTakeAction(); openAddToExistingCaseFromFlyout(); selectExistingCase(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/indicators.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/indicators.cy.ts index 53a4f454ac782..b0e5764469459 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/indicators.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/threat_intelligence/indicators.cy.ts @@ -305,8 +305,7 @@ describe('Multiple indicators', { tags: ['@ess'] }, () => { }); }); -// FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/209039 -describe.skip('Invalid Indicators', { tags: ['@ess'] }, () => { +describe('Invalid Indicators', { tags: ['@ess'] }, () => { before(() => cy.task('esArchiverLoad', { archiveName: 'ti_indicators_data_invalid' })); after(() => cy.task('esArchiverUnload', { archiveName: 'ti_indicators_data_invalid' })); diff --git a/x-pack/test/security_solution_cypress/es_archives/ti_indicators_data_invalid/data.json b/x-pack/test/security_solution_cypress/es_archives/ti_indicators_data_invalid/data.json index 664ef1b35417f..9d08294a2e0cb 100644 --- a/x-pack/test/security_solution_cypress/es_archives/ti_indicators_data_invalid/data.json +++ b/x-pack/test/security_solution_cypress/es_archives/ti_indicators_data_invalid/data.json @@ -5,7 +5,7 @@ "id": "RP0HlUQkToBRTlZeGAItbyWMx1E=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.677Z", + "@timestamp": "2022-06-02T13:29:47.685Z", "abusech": { "malware": { } @@ -97,7 +97,7 @@ "id": "C4ObxkoTZzcjmk1jFwGlRadzMnA=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.678Z", + "@timestamp": "2022-06-02T13:29:47.684Z", "abusech": { "malware": { "virustotal": { @@ -193,7 +193,7 @@ "id": "5hGL0ETQsk+B0L7ryVcQVwsYhOk=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.678Z", + "@timestamp": "2022-06-02T13:29:47.683Z", "abusech": { "malware": { "virustotal": { @@ -288,7 +288,7 @@ "id": "qq3AKvjp1c/FBtEoh10Vt+PsT14=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.678Z", + "@timestamp": "2022-06-02T13:29:47.682Z", "abusech": { "malware": { } @@ -377,7 +377,7 @@ "id": "CNCiNUxTNHF5qyRWclltlrnxwhk=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.678Z", + "@timestamp": "2022-06-02T13:29:47.681Z", "abusech": { "malware": { "virustotal": { @@ -470,7 +470,7 @@ "id": "Rk80kuvgnMegEB+1jhGlgLO5h5Y=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.680Z", "abusech": { "malware": { "virustotal": { @@ -562,7 +562,7 @@ "id": "oF2/6vlWcu7040SDtfZuBX4sXEo=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.679Z", "abusech": { "malware": { } @@ -648,7 +648,7 @@ "id": "mgUWSsWrUtqPZFUpBNhFU75TKyc=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.678Z", "abusech": { "malware": { } @@ -733,7 +733,7 @@ "id": "auKnqhqoLKmnMsohKHQMvqvLSK4=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.677Z", "abusech": { "malware": { } @@ -817,7 +817,7 @@ "id": "Llc8xZPNZbUM6j5sAHAFCeyu+po=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.676Z", "abusech": { "malware": { "virustotal": { @@ -905,7 +905,7 @@ "id": "VbMLdKEoQI/Xli/LgjmvMOlGYZY=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.675Z", "abusech": { "malware": { } @@ -987,7 +987,7 @@ "id": "/6DDSx9lUsUoJUF8QSZiQ/oMvmQ=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.681Z", + "@timestamp": "2022-06-02T13:29:47.674Z", "abusech": { "malware": { } @@ -1068,7 +1068,7 @@ "id": "d+KIrgaxYVhvb/sqhlb5AYOHQDo=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.673Z", "abusech": { "malware": { } @@ -1148,7 +1148,7 @@ "id": "bAPvrGKWj/ess46s3KwFqAJ8+tc=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.672Z", "abusech": { "malware": { } @@ -1227,7 +1227,7 @@ "id": "uM8A5Yr/gMJ4tPHb9XIABYC/mRk=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.671Z", "abusech": { "malware": { } @@ -1305,7 +1305,7 @@ "id": "YPTTIf8ctfvqnTo2W9OpoJD6n9Q=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.670Z", "abusech": { "malware": { } @@ -1382,7 +1382,7 @@ "id": "UJBUWYV6AtCidXCm1NBsWtAYWZI=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.669Z", "abusech": { "malware": { } @@ -1458,7 +1458,7 @@ "id": "e1yn2nAO9PlprMEaPBhcjgg9lwE=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.668Z", "abusech": { "malware": { } @@ -1533,7 +1533,7 @@ "id": "e74l+UPbo6o0DotQc8Roo3OVcJQ=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.667Z", "abusech": { "malware": { "virustotal": { @@ -1612,7 +1612,7 @@ "id": "XIDmYG67Bs5j3njl7xYKAyH1emM=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.682Z", + "@timestamp": "2022-06-02T13:29:47.666Z", "abusech": { "malware": { "virustotal": { @@ -1684,7 +1684,7 @@ "id": "TxxcH4E0aWG8D8rloVjU3cK+sy0=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.685Z", + "@timestamp": "2022-06-02T13:29:47.665Z", "abusech": { "malware": { "virustotal": { @@ -1750,7 +1750,7 @@ "id": "0j+BQ8HFrDQYe5kbXMc9ANSCjBY=", "index": ".ds-logs-ti_abusech.malware-default-2022.06.02-000001", "source": { - "@timestamp": "2022-06-02T13:29:47.685Z", + "@timestamp": "2022-06-02T13:29:47.664Z", "abusech": { "malware": { "virustotal": { diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 77ffa8f2d7b3e..5881f08f5accd 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -106,7 +106,6 @@ "@kbn/apm-synthtrace-client", "@kbn/utils", "@kbn/journeys", - "@kbn/stdio-dev-helpers", "@kbn/alerting-api-integration-helpers", "@kbn/cloud-security-posture-plugin", "@kbn/cloud-integration-saml-provider-plugin", diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts index 6a0f8aad4686e..d29435ce35843 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts @@ -42,13 +42,13 @@ export default function ({ getService }: FtrProviderContext) { // await kibanaServer.savedObjects.cleanStandardList(); await esArchiver.emptyKibanaIndex(); }); - it('returns does not return api deprecations if the routes are not called', async () => { + it('does not return api deprecations if deprecated routes are not called', async () => { const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; const apiDeprecations = getApiDeprecations(deprecations); expect(apiDeprecations.length).to.equal(0); }); - it('returns deprecated APIs when the api is called', async () => { + it('returns deprecated APIs when a deprecated api is called', async () => { await supertest .get(`/internal/routing_example/d/internal_versioned_route?apiVersion=1`) .expect(200); @@ -206,6 +206,25 @@ export default function ({ getService }: FtrProviderContext) { ); }); }); + it('GET /api/upgrade_assistant/status does not return { readyForUpgrade: false } if there are only critical API deprecations', async () => { + /** Throw in another critical deprecation... */ + await supertest.get(`/api/routing_example/d/removed_route`).expect(200); + // sleep a little until the usage counter is synced into ES + await setTimeoutAsync(3000); + await retry.tryForTime( + 15 * 1000, + async () => { + const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body; + const apiDeprecations = getApiDeprecations(deprecations); + // confirm there is at least one CRITICAL deprecated API usage present + expect(apiDeprecations.some(({ level }) => level === 'critical')).to.be(true); + }, + undefined, + 2000 + ); + const { body } = await supertest.get(`/api/upgrade_assistant/status`).expect(200); + expect(body.readyForUpgrade).to.be(true); + }); }); } diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts index ddc03dbde297a..b0676359409ec 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts @@ -8,9 +8,9 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { - // FAILING VERSION BUMP: https://github.com/elastic/kibana/issues/209048 - describe.skip('upgrade assistant', function () { - loadTestFile(require.resolve('./reindexing')); + describe('upgrade assistant', function () { + // FAILING VERSION BUMP: https://github.com/elastic/kibana/issues/209048 + // loadTestFile(require.resolve('./reindexing')); loadTestFile(require.resolve('./api_deprecations')); }); } diff --git a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_details.ts b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_details.ts index 8904ffbfe958a..4d6a6e19a772a 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_details.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/dataset_quality/dataset_quality_details.ts @@ -60,7 +60,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Dataset quality details', function () { // see details: https://github.com/elastic/kibana/issues/206734 - this.tags(['failsOnMKI']); + before(async () => { // Install Apache Integration and ingest logs for it await PageObjects.observabilityLogsExplorer.installPackage(apachePkg); @@ -326,22 +326,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); describe('navigation', () => { - it('should go to log explorer page when the open in log explorer button is clicked', async () => { + it('should go to discover page when the open in discover button is clicked', async () => { await PageObjects.datasetQuality.navigateToDetails({ dataStream: regularDataStreamName, }); - const logExplorerButton = + const discoverButton = await PageObjects.datasetQuality.getDatasetQualityDetailsHeaderButton(); - await logExplorerButton.click(); + await discoverButton.click(); + + // Confirm dataset selector text in discover + await retry.tryForTime(5000, async () => { + const datasetSelectorText = await PageObjects.discover.getCurrentDataViewId(); - // Confirm dataset selector text in observability logs explorer - const datasetSelectorText = await PageObjects.discover.getCurrentDataViewId(); - originalExpect(datasetSelectorText).toMatch(regularDatasetName); + originalExpect(datasetSelectorText).toMatch(regularDatasetName); + }); }); - it('should go log explorer for degraded docs when the button next to breakdown selector is clicked', async () => { + it('should go discover for degraded docs when the button next to breakdown selector is clicked', async () => { await PageObjects.datasetQuality.navigateToDetails({ dataStream: apacheAccessDataStreamName, }); @@ -350,9 +353,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { PageObjects.datasetQuality.testSubjectSelectors.datasetQualityDetailsLinkToDiscover ); - // Confirm dataset selector text in observability logs explorer - const datasetSelectorText = await PageObjects.discover.getCurrentDataViewId(); - originalExpect(datasetSelectorText).toMatch(apacheAccessDatasetName); + // Confirm dataset selector text in discover + await retry.tryForTime(5000, async () => { + const datasetSelectorText = await PageObjects.discover.getCurrentDataViewId(); + originalExpect(datasetSelectorText).toMatch(apacheAccessDatasetName); + }); }); }); diff --git a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts index ef7074e7b44e1..97034bb51c502 100644 --- a/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts +++ b/x-pack/test_serverless/functional/test_suites/search/search_index_detail.ts @@ -26,7 +26,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const esDeleteAllIndices = getService('esDeleteAllIndices'); const indexName = 'test-my-index'; - describe('index details page - search solution', function () { + // Failing: See https://github.com/elastic/kibana/issues/203508 + describe.skip('index details page - search solution', function () { describe('developer', function () { before(async () => { await pageObjects.svlCommonPage.loginWithRole('developer'); diff --git a/yarn.lock b/yarn.lock index 1be1c9bb01be6..1938e451f6eea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6012,6 +6012,10 @@ version "0.0.0" uid "" +"@kbn/inference-langchain@link:x-pack/platform/packages/shared/ai-infra/inference-langchain": + version "0.0.0" + uid "" + "@kbn/inference-plugin@link:x-pack/platform/plugins/shared/inference": version "0.0.0" uid "" @@ -7304,10 +7308,6 @@ version "0.0.0" uid "" -"@kbn/serverless-project-switcher@link:src/platform/packages/private/serverless/project_switcher": - version "0.0.0" - uid "" - "@kbn/serverless-search-settings@link:src/platform/packages/shared/serverless/settings/search_project": version "0.0.0" uid "" @@ -19460,10 +19460,10 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0, form-data@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== +form-data@^4.0.0, form-data@^4.0.1, form-data@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -24657,10 +24657,10 @@ moment-duration-format@^2.3.2: resolved "https://registry.yarnpkg.com/moment-duration-format/-/moment-duration-format-2.3.2.tgz#5fa2b19b941b8d277122ff3f87a12895ec0d6212" integrity sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ== -moment-timezone@^0.5.46: - version "0.5.46" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.46.tgz#a21aa6392b3c6b3ed916cd5e95858a28d893704a" - integrity sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw== +moment-timezone@^0.5.47: + version "0.5.47" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.47.tgz#d4d1a21b78372d914d6d69ae285454732a429749" + integrity sha512-UbNt/JAWS0m/NJOebR0QMRHBk0hu03r5dx9GK8Cs0AS3I81yDcOc9k+DytPItgVvBP7J6Mf6U2n3BPAacAV9oA== dependencies: moment "^2.29.4" @@ -25414,10 +25414,10 @@ oas-validator@^5.0.8: should "^13.2.1" yaml "^1.10.0" -oas@^25.2.1: - version "25.2.1" - resolved "https://registry.yarnpkg.com/oas/-/oas-25.2.1.tgz#2cf0ac042e5ba7af0bfc2a9acea6d33699cd6b90" - integrity sha512-56NO/ThEzIQeniJHm12tFIIGaugcdZTxYWiWP0KV8W9DcdWRvk9tvBEKITFp9r62w7W25vobedpiApa9u/Aoxg== +oas@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/oas/-/oas-25.3.0.tgz#c55a3a557fc040c2e8ea5954dd017b746ae2ac3a" + integrity sha512-98HLqUASE1u4kS8o3O7ZqjT1LG5oNwNjG1oXTsMscYVl2RSW4gMzlrn80dpxjsxpMlVuqPPa/sUS5WFtkk+Xxg== dependencies: "@readme/json-schema-ref-parser" "^1.2.0" "@types/json-schema" "^7.0.11" @@ -27767,10 +27767,10 @@ react-remove-scroll@^2.5.6: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" -react-reverse-portal@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/react-reverse-portal/-/react-reverse-portal-2.1.2.tgz#75b8daa186ccb9a040ba566d6391326431fccd51" - integrity sha512-li4puNtBmMMJhtI+IVxeSX0RvK1ft8qjPSbCih4OKQ/YUIcROc31Nmo22gv94hTx8EUfR7fzZY47RuZF2YRMdQ== +react-reverse-portal@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/react-reverse-portal/-/react-reverse-portal-2.2.0.tgz#0a119f8ab00885fa8aebc03a9fd2111d0e76303c" + integrity sha512-y4odW09uzTtyJWs9E9YbY50FypJeRzEMhEjQUpzLOXZeTHCFE88WfklvRUddTJsizFWTzMOLTE4wNgYdzeQ7uw== react-router-config@^5.1.1: version "5.1.1"